From :
AWK:Linux 管理员的智能工具包
作者 Emmett Dulaney
您正在学习 Linux 吗?本文对于非常有用的 AWK 文本操作工具进行了介绍,非常有价值。
AWK 实用工具带有其自己的自包含语言,它不仅是 Linux 中也是任何环境中现有的功能最强大的数据处理引擎之一。这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
AWK 是什么?
最简单地说,AWK 是一种用于处理文本的编程语言工具。AWK 实用工具的语言在很多方面类似于 shell 编程语言,尽管 AWK 具有完全属于其本身的语法。在最初创造 AWK 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。
尽管操作可能会很复杂,但命令的语法始终是:
awk '{pattern + action}' {filenames}
其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号 ({}) 不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。
了解字段
实用工具将每个输入行分为记录和字段。记录是单行的输入,而每条记录包含若干字段。默认的字段分隔符是空格或制表符,而记录的分隔符是换行。虽然在默认情况下将制表符和空格都看作字段分隔符(多个空格仍然作为一个分隔符),但是可以将分隔符从空格改为任何其它字符。
为了进行演示,请查看以下保存为 emp_names 的员工列表文件:
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
46019 BOGUE ROBERT PHOENIX AZ
46021 JUNE MICAH PHOENIX AZ
46022 KANE SHERYL UNKNOWN AR
46024 WOOD WILLIAM MUNCIE IN
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
46029 TUTTLE BOB MUNCIE IN
当 AWK 读取输入内容时,整条记录被分配给变量 $0。每个字段以字段分隔符分开,被分配给变量 $1、$2、$3 等等。一行在本质上可以包含无数个字段,通过字段号来访问每个字段。因此,命令
awk '{print $1,$2,$3,$4,$5}' names
将会产生的打印输出是
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
46019 BOGUE ROBERT PHOENIX AZ
46021 JUNE MICAH PHOENIX AZ
46022 KANE SHERYL UNKNOWN AR
46024 WOOD WILLIAM MUNCIE IN
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
46029 TUTTLE BOB MUNCIE IN
值得注意的一项重要内容是,AWK 解释由空格分隔的五个字段,但当它打印显示内容时,在每个字段间只有一个空格。利用为每个字段指定了唯一号码的功能,您可以选择只打印特定的字段。例如,只打印每条记录的姓名时,只需选择第二个和第三个字段进行打印:
$ awk '{print $2,$3}' emp_names
DULANEY EVAN
DURHAM JEFF
STEEN BILL
FELDMAN EVAN
SWIM STEVE
BOGUE ROBERT
JUNE MICAH
KANE SHERYL
WOOD WILLIAM
FERGUS SARAH
BUCK SARAH
TUTTLE BOB
$
您还可以指定按任何顺序打印字段,而无论它们在记录中是如何存在的。因此,只需要显示姓名字段,并且使其顺序颠倒,先显示名字再显示姓氏:
$ awk '{print $3,$2}' emp_names
EVAN DULANEY
JEFF DURHAM
BILL STEEN
EVAN FELDMAN
STEVE SWIM
ROBERT BOGUE
MICAH JUNE
SHERYL KANE
WILLIAM WOOD
SARAH FERGUS
SARAH BUCK
BOB TUTTLE
$
使用模式
通过包含一个必须匹配的模式,您可以选择只对特定的记录而不是所有的记录进行操作。模式匹配的最简单形式是搜索,其中要匹配的项目被包含在斜线 (/pattern/) 中。例如,只对那些居住在阿拉巴马州的员工执行前面的操作:
$ awk '/AL/ {print $3,$2}' emp_names
EVAN DULANEY
JEFF DURHAM
BILL STEEN
EVAN FELDMAN
STEVE SWIM
$
如果您不指定要打印的字段,则会打印整个匹配的条目:
$ awk '/AL/' emp_names
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
$
对同一数据集的多个命令可以用分号 (;) 分隔开。例如,在一行中打印姓名,而在另一行中打印城市和州名:
$ awk '/AL/ {print $3,$2 ; print $4,$5}' emp_names
EVAN DULANEY
MOBILE AL
JEFF DURHAM
MOBILE AL
BILL STEEN
MOBILE AL
EVAN FELDMAN
MOBILE AL
STEVE SWIM
UNKNOWN AL
$
如果没有使用分号 (print $3,$2,$4,$5),则会在同一行中显示所有内容。另一方面,如果分别给出两个打印语句,则会产生完全不同的结果:
$ awk '/AL/ {print $3,$2} {print $4,$5}' emp_names
EVAN DULANEY
MOBILE AL
JEFF DURHAM
MOBILE AL
BILL STEEN
MOBILE AL
EVAN FELDMAN
MOBILE AL
STEVE SWIM
UNKNOWN AL
PHOENIX AZ
PHOENIX AZ
UNKNOWN AR
MUNCIE IN
MUNCIE IN
MUNCIE IN
MUNCIE IN
$
只有在列表中找到 AL 时才会给出字段三和字段二。但是,字段四和字段五是无条件的,始终打印它们。只有第一组花括号中的命令对前面紧邻的命令 (/AL/) 起作用。
结果非常不便于阅读,可以使其稍微更清晰一些。首先,在城市与州之间插入一个空格和逗号。然后,在每两行显示之后放置一个空行:
$ awk '/AL/ {print $3,$2 ; print $4", "$5"\n"}' emp_names
EVAN DULANEY
MOBILE, AL
JEFF DURHAM
MOBILE, AL
BILL STEEN
MOBILE, AL
EVAN FELDMAN
MOBILE, AL
STEVE SWIM
UNKNOWN, AL
$
在第四和第五个字段之间,添加一个逗号和一个空格(在引号之间),在第五个字段后面,打印一个换行符 (\n)。在 AWK 打印语句中还可以使用那些可在 echo 命令中使用的所有特殊字符,包括:
* \n(换行)
* \t(制表)
* \b(退格)
* \f(进纸)
* \r(回车)
因此,要读取全部五个最初由制表符分隔开的字段,并且也利用制表符打印它们,您可以编程如下
$ awk '{print $1"\t"$2"\t"$3"\t"$4"\t"$5}' emp_names
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
46019 BOGUE ROBERT PHOENIX AZ
46021 JUNE MICAH PHOENIX AZ
46022 KANE SHERYL UNKNOWN AR
46024 WOOD WILLIAM MUNCIE IN
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
46029 TUTTLE BOB MUNCIE IN
$
通过连续设置多项标准并用管道 (|) 符号将其分隔开,您可以一次搜索多个模式匹配:
$ awk '/AL|IN/' emp_names
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
46024 WOOD WILLIAM MUNCIE IN
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
46029 TUTTLE BOB MUNCIE IN
$
这样可找到每个阿拉巴马州和印第安那州居民的匹配记录。但是在试图找出居住在亚利桑那州的人时,出现了一个问题:
$ awk '/AR/' emp_names
46019 BOGUE ROBERT PHOENIX AZ
46021 JUNE MICAH PHOENIX AZ
46022 KANE SHERYL UNKNOWN AZ
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
$
员工 46026 和 46027 没有住在亚利桑那州;但是他们的名字中包含所搜索的字符序列。切记,当在 AWK 中进行模式匹配时,例如 grep、sed 或者大部分其他 Linux/Unix 命令,将在记录(行)中的任何位置查找匹配,除非指定进行其他操作。为解决这一问题,必须将搜索与特定字段联系起来。通过利用代字号 (˜) 以及对特定字段的说明,可以达到这一目的,如下例所示:
$ awk '$5 ˜ /AR/' emp_names
46019 BOGUE ROBERT PHOENIX AZ
46021 JUNE MICAH PHOENIX AZ
46022 KANE SHERYL UNKNOWN AZ
$
代字号(表示匹配)的对应符号是一个前面带有感叹号的代字号 (!˜)。这些字符通知程序,如果搜索序列没有出现在指定字段中,则找出与搜索序列相匹配的所有行:
$ awk '$5 !˜ /AR/' names
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
46017 FELDMAN EVAN MOBILE AL
46018 SWIM STEVE UNKNOWN AL
46024 WOOD WILLIAM MUNCIE IN
46026 FERGUS SARAH MUNCIE IN
46027 BUCK SARAH MUNCIE IN
46029 TUTTLE BOB MUNCIE IN
$
在这种情况下,将显示第五个字段中没有 AR 的所有行 — 包括两个 Sarah 条目,这两个条目确实包含 AR,但却是在第三个字段而不是第五个字段中。
花括号和字段分隔符
括号字符在 AWK 命令中起着很重要的作用。出现在括号之间的操作指出将要发生什么以及何时发生。当只使用一对括号时:
{print $3,$2}
括号间的所有操作同时发生。当使用多于一对的括号时:
{print $3}{print $2}
执行第一组命令,在该命令完成后执行第二组命令。注意以下两列清单的区别:
$ awk '{print $3,$2}' names
EVAN DULANEY
JEFF DURHAM
BILL STEEN
EVAN FELDMAN
STEVE SWIM
ROBERT BOGUE
MICAH JUNE
SHERYL KANE
WILLIAM WOOD
SARAH FERGUS
SARAH BUCK
BOB TUTTLE
$
$ awk '{print $3}{print $2}' names
EVAN
DULANEY
JEFF
DURHAM
BILL
STEEN
EVAN
FELDMAN
STEVE
SWIM
ROBERT
BOGUE
MICAH
JUNE
SHERYL
KANE
WILLIAM
WOOD
SARAH
FERGUS
SARAH
BUCK
BOB
TUTTLE
$
要利用多组括号进行重复查找,执行第一组中的命令直到完成为止;然后处理第二组命令。如果有第三组命令,则在第二组命令完成后执行它,以此类推。在所生成的打印输出中,有两个分隔的打印命令,因此先执行第一个命令,随后执行第二个命令,这样导致每个条目显示在两行而不是一行中。
区分两个字段的字段分隔符不一定始终是空格;它可以是任何可识别的字符。为进行演示,假定 emp_names 文件利用冒号而不是制表符来分隔字段:
$ cat emp_names
46012:DULANEY:EVAN:MOBILE:AL
46013:DURHAM:JEFF:MOBILE:AL
46015:STEEN:BILL:MOBILE:AL
46017:FELDMAN:EVAN:MOBILE:AL
46018:SWIM:STEVE:UNKNOWN:AL
46019:BOGUE:ROBERT:PHOENIX:AZ
46021:JUNE:MICAH:PHOENIX:AZ
46022:KANE:SHERYL:UNKNOWN:AR
46024:WOOD:WILLIAM:MUNCIE:IN
46026:FERGUS:SARAH:MUNCIE:IN
46027:BUCK:SARAH:MUNCIE:IN
46029:TUTTLE:BOB:MUNCIE:IN
$
如果试图通过指定所需要的第二个字段来打印姓氏
$ awk '{print $2}' emp_names
您最后会得到十二个空行。因为文件中没有空格,除了第一个字段之外没有可认别的字段。为解决这一问题,必须通知 AWK 是空格之外的另一个字符作为分隔符,有两种方法可通知 AWK 使用新的字段分隔符:使用命令行参数 -F,或在程序中指定变量 FS。两种方法的效果相同,只有一种例外情况,如下例所示:
$ awk '{FS=":"}{print $2}' emp_names
DURHAM
STEEN
FELDMAN
SWIM
BOGUE
JUNE
KANE
WOOD
FERGUS
BUCK
TUTTLE
$
$ awk -F: '{print $2}' emp_names
DULANEY
DURHAM
STEEN
FELDMAN
SWIM
BOGUE
JUNE
KANE
WOOD
FERGUS
BUCK
TUTTLE
$
在第一个命令中,头一条记录返回不正确的空行,而其他结果正确。直到读取第二条记录时,才识别字段分隔符并正确地执行。通过使用 BEGIN 语句可以纠正这一缺点(在后文详述)。-F 的功能非常类似于 BEGIN,能够正确地读取第一条记录并按要求执行。
在本文开始处我曾提到,默认的显示/输出字段分隔符是空格。通过使用输出字段分隔符 (OFS) 变量,可以在程序中更改此特性。例如,要读取文件(由冒号分隔)并以短划线显示,则命令是
$ awk -F":" '{OFS="-"}{print $1,$2,$3,$4,$5}' emp_names
46012-DULANEY-EVAN-MOBILE-AL
46013-DURHAM-JEFF-MOBILE-AL
46015-STEEN-BILL-MOBILE-AL
46017-FELDMAN-EVAN-MOBILE-AL
46018-SWIM-STEVE-UNKNOWN-AL
46019-BOGUE-ROBERT-PHOENIX-AZ
46021-JUNE-MICAH-PHOENIX-AZ
46022-KANE-SHERYL-UNKNOWN-AR
46024-WOOD-WILLIAM-MUNCIE-IN
46026-FERGUS-SARAH-MUNCIE-IN
46027-BUCK-SARAH-MUNCIE-IN
46029-TUTTLE-BOB-MUNCIE-IN
$
FS 和 OFS 是(输入)字段分隔符和输出字段分隔符,它们只是一对可以在 AWK 实用工具中使用的变量。例如,要在打印时为每行编号,可以采用以下方式使用 NR 变量:
$ awk -F":" '{print NR,$1,$2,$3}' emp_names
1 46012 DULANEY EVAN
2 46013 DURHAM JEFF
3 46015 STEEN BILL
4 46017 FELDMAN EVAN
5 46018 SWIM STEVE
6 46019 BOGUE ROBERT
7 46021 JUNE MICAH
8 46022 KANE SHERYL
9 46024 WOOD WILLIAM
10 46026 FERGUS SARAH
11 46027 BUCK SARAH
12 46029 TUTTLE BOB
$
找出员工号码处于 46012 和 46015 之间的所有行:
$ awk -F":" '/4601[2-5]/' emp_names
46012 DULANEY EVAN MOBILE AL
46013 DURHAM JEFF MOBILE AL
46015 STEEN BILL MOBILE AL
$
添加文本
可以按照添加控制序列或其他字符的相同方式将文本添加到显示中。例如,要将分隔符从空格改为冒号,则命令是
awk '{print $1":"$2":"$3":"$4":"$5}' emp_names > new_emp_names
在这种情况下,字符 (:) 包含在引号 ("/") 中,它被添加到每个字段之间。在引号之间的值可以是任何内容。例如,创建一个关于居住在阿拉巴马州的员工的外观类似数据库的显示:
$ awk '$5 ~ /AL/ {print "NAME: "$2", "$3"\nCITY-STATE:
"$4", "$5"\n"}' emp_names
NAME: DULANEY, EVAN
CITY-STATE: MOBILE, AL
NAME: DURHAM, JEFF
CITY-STATE: MOBILE, AL
NAME: STEEN, BILL
CITY-STATE: MOBILE, AL
NAME: FELDMAN, EVAN
CITY-STATE: MOBILE, AL
NAME: SWIM, STEVE
CITY-STATE: UNKNOWN, AL
$
数学操作
AWK 除了提供文本功能,还提供全部范围的算术操作符,包括以下符号:
+ 将数字相加
- 减
* 乘
/ 除
^ 执行指数运算
% 提供模
++ 将变量值加一
+= 将其他操作的结果分配给变量
— 将变量减一
-= 将减法操作的结果分配给变量
*= 分配乘法操作的结果
/= 分配除法操作的结果
%= 分配求模操作的结果
例如,假定您的机器上存在以下的文件,详细地列出硬件商店中的物品:
$ cat inventory
hammers 5 7.99
drills 2 29.99
punches 7 3.59
drifts 2 4.09
bits 55 1.19
saws 123 14.99
nails 800 .19
screws 80 .29
brads 100 .24
$
第一项业务定单是通过将第二个字段(数量)的值乘以第三个字段(价格)的值,计算每种物品的库存价值:
$ awk '{print $1,"QTY: "$2,"PRICE: "$3,"TOTAL: "$2*$3}' inventory
hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95
drills QTY: 2 PRICE: 29.99 TOTAL: 59.98
punches QTY: 7 PRICE: 3.59 TOTAL: 25.13
drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18
bits QTY: 55 PRICE: 1.19 TOTAL: 65.45
saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77
nails QTY: 800 PRICE: .19 TOTAL: 152
screws QTY: 80 PRICE: .29 TOTAL: 23.2
brads QTY: 100 PRICE: .24 TOTAL: 24
$
如果这些行本身并不重要,您只是希望确定商店中有多少件物品,则可以分配一个普通变量,按照每条记录中的物品数量增加:
$ awk '{x=x+$2} {print x}' inventory
5
7
14
16
71
194
994
1074
1174
$
根据这一数据,商店中有 1174 件物品。第一次执行时,变量 x 没有值,因此它采用第一行第二个字段的值。第二次执行时,它保留了第一行的值并加上第二行的值,以此类推,直到达到累计的总合。
可以应用相同的过程来确定现有库存的总价值:
$ awk '{x=x+($2*$3)} {print x}' inventory
39.95
99.93
125.06
133.24
198.69
2042.46
2194.46
2217.66
2241.66
$
因此,1174 件物品的价值是 $2,241.66。虽然这一过程可以获得总计值,但它的外观很差,需要加工成实际的报表。利用一些附加项,很容易使显示变得更整洁:
$ awk '{x=x+($2*$3)}{print $1,"QTY: "$2,"PRICE: "$3,"TOTAL: "$2*$3,"BAL: "x}' inventory
hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95 BAL: 39.95
drills QTY: 2 PRICE: 29.99 TOTAL: 59.98 BAL: 99.93
punches QTY: 7 PRICE: 3.59 TOTAL: 25.13 BAL: 125.06
drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18 BAL: 133.24
bits QTY: 55 PRICE: 1.19 TOTAL: 65.45 BAL: 198.69
saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77 BAL: 2042.46
nails QTY: 800 PRICE: .19 TOTAL: 152 BAL: 2194.46
screws QTY: 80 PRICE: .29 TOTAL: 23.2 BAL: 2217.66
brads QTY: 100 PRICE: .24 TOTAL: 24 BAL: 2241.66
$
该过程提供了每条记录的清单,同时将总价值分配给库存值,并保持商店资产的运作平衡。
BEGIN 和 END
使用 BEGIN 和 END 语句可以分别指定在处理实际开始之前或者完成之后进行操作。BEGIN 语句最常用于建立变量或显示标题。另一方面,END 语句可用于在程序结束后继续进行处理。
在前面的示例中,利用以下例程生成了物品的总价值:
awk '{x=x+($2*$3)} {print x}' inventory
该例程在运行总计累加时显示了文件中的每一行。没有其他方法可以指定它,而不让在每一行进行打印也导致它始终不打印出来。但是,利用 END 语句可以避免这一问题:
$ awk '{x=x+($2*$3)} END {print "Total Value of Inventory:"x}' inventory
Total Value of Inventory: 2241.66
$
定义了变量 x,它对每一行进行处理;但是,在所有处理完成之前不会生成显示。尽管可以作为独立例程使用,它也可以置入到先前的代码列表,添加更多信息并生成更完整的报表:
$ awk '{x=x+($2*$3)} {print $1,"QTY: "$2,"PRICE:
"$3,"TOTAL: "$2*$3} END {print "Total Value of Inventory: " x}' inventory
hammers QTY: 5 PRICE: 7.99 TOTAL: 39.95
drills QTY: 2 PRICE: 29.99 TOTAL: 59.98
punches QTY: 7 PRICE: 3.59 TOTAL: 25.13
drifts QTY: 2 PRICE: 4.09 TOTAL: 8.18
bits QTY: 55 PRICE: 1.19 TOTAL: 65.45
saws QTY: 123 PRICE: 14.99 TOTAL: 1843.77
nails QTY: 800 PRICE: .19 TOTAL: 152
screws QTY: 80 PRICE: .29 TOTAL: 23.2
brads QTY: 100 PRICE: .24 TOTAL: 24
Total Value of Inventory: 2241.66
$
BEGIN 命令与 END 的工作方式相同,但它建立了那些需要在完成其他工作之前所做的项目。该过程最常见的目的是创建报表的标题。此例程的语法类似于
$ awk 'BEGIN {print "ITEM QUANTITY PRICE TOTAL"}'
输入、输出和源文件
AWK 工具可以从文件中读取其输入,正如在此之前所有示例所做的那样,它也可以从其他命令的输出中获取输入。例如:
$ sort emp_names | awk '{print $3,$2}'
awk 命令的输入是排序操作的输出。除了 sort,还可以使用任何其他的 Linux 命令 — 例如 grep。该过程允许您在离开所选定字段前对文件执行其他操作。
类似于解释程序,AWK 使用输出改向操作符 > 和 >> 将其输出放入文件中而不是标准输出设备。这些符号的作用类似于它们在解释程序中的对应符号,因此 > 在不存在文件时创建文件,而 >> 追加到现有文件的尾部。请看以下的示例:
$ awk '{print NR, $1 ) > "/tmp/filez" }' emp_names
$ cat /tmp/filez
1 46012
2 46013
3 46015
4 46017
5 46018
6 46019
7 46021
8 46022
9 46024
10 46026
11 46027
12 46029
$
检查该语句的语法,您会看到输出改向是在打印语句完成后进行的。必须将文件名包含在引号中,否则它只是一个未初始化的 AWK 变量,而将指令联接起来会在 AWK 中产生错误。(如果不正确地使用改向符号,则 AWK 无法了解该符号意味着“改向”还是一个关系操作符。)
在 AWK 中输出到管道也类似于解释程序中所实现的相同操作。要将打印命令的输出发送到管道中,可以在打印命令后附加管道符号以及命令的名称,如下所示:
$ awk '{ print $2 | "sort" }' emp_names
BOGUE
BUCK
DULANEY
DURHAM
FELDMAN
FERGUS
JUNE
KANE
STEEN
SWIM
TUTTLE
WOOD
$
这是输出改向的情况,必须将命令包含在引号中,而管道的名称是被执行命令的名称。
AWK 所使用的命令可以来自两个地方。首先,可以在命令行中指定它们,如示例中所示。其次,它们可以由源文件提供。如果是这种情况,通过 -f 选项将这种情况向 AWK 发出警告。演示如下:
$ cat awklist
{print $3,$2}
{print $4,$5,"\n"}
$
$ awk -f awklist emp_names
EVAN DULANEY
MOBILE AL
JEFF DURHAM
MOBILE AL
BILL STEEN
MOBILE AL
EVAN FELDMAN
MOBILE AL
STEVE SWIM
UNKNOWN AL
ROBERT BOGUE
PHOENIX AZ
MICAH JUNE
PHOENIX AZ
SHERYL KANE
UNKNOWN AR
WILLIAM WOOD
MUNCIE IN
SARAH FERGUS
MUNCIE IN
SARAH BUCK
MUNCIE IN
BOB TUTTLE
MUNCIE IN
$
注意,在源文件中的任何地方或者在命令行中调用它时,不使用单引号。单引号只用于区别命令行中的命令与文件名称。
如果简单的输出不能处理您的程序中所需要的复杂信息,则可以尝试由 printf 命令获得的更加复杂的输出,其语法是
printf( format, value, value ...)
该语法类似于 C 语言中的 printf 命令,而格式的规格是相同的。通过插入一项定义如何打印数值的规格,可以定义该格式。格式规格包含一个跟有字母的 %。类似于打印命令,printf 不必包含在圆括号中,但是可以认为使用圆括号是一种良好的习惯。
下表列出 printf 命令提供的各种规格。
规格 说明
%c 打印单个 ASCII 字符
%d 打印十进制数
%e 打印数字的科学计数表示
%f 打印浮点表示
%g 打印 %e 或 %f;两种方式都更简短
%o 打印无符号的八进制数
s 打印 ASCII 字符串
%x 打印无符号的十六进制数
%% 打印百分号;不执行转换
可以在 % 与字符之间提供某些附加的格式化参数。这些参数进一步改进数值的打印方式:
参数 说明
- 将字段中的表达式向左对齐
,width 根据需要将字段补齐到指定宽度(前导零使用零将字段补齐)
.prec 小数点右面数字的最大字符串宽度或最大数量
printf 命令能够控制并将数值从一种格式转换为另一种格式。当需要打印变量的值时,只需提供一种规格,指示 printf 如何打印信息(通常包含在双引号中)即可。必须为每个传递到 printf 的变量包含一个规格参数;如果包含过少的参数,则 printf 不会打印所有的数值。
处理错误
AWK 工具报告所发生错误的方式很令人恼火。一个错误会阻碍任何操作的进行,所提供的错误信息非常含混不清:
awk: syntax error near line 2
awk: bailing out near line 2
您可能会花几小时的时间查看第 2 行,试图找出它为什么阻碍程序运行;这就是支持使用源文件的一个有力论据。
切记有两条规则可以帮助您避免出现语法错误:
1. 确保命令位于括号中,而括号位于单引号中。没有使用这些字符之一必然导致程序无法运行。
2. 搜索命令需要位于斜线之间。要找出住在印第安那州的员工,您必须使用“/IN/”而不是“IN”。
结论
尽管 AWK 完全代表另外的含意,但它应该是管理员智能工具包的首字母缩写。连同 SED 一起,AWK 实用工具是 Linux 管理员所拥有的功能最强大和灵活的工具之一。通过了解其语言的一些特性,您可以开辟出能够简化任务的领域,否则这些任务将会是非常费时和困难的。
==========================================================
awk详解
a w k是一种程序语言,对文档资料的处理具有很强的功能。awk 名称是由它三个最初设计
者的姓氏的第一个字母而命名的: Alfred V. Aho、Peter J. We i n b e rg e r、Brian W. Kernighan。
a w k最初在1 9 7 7年完成。1 9 8 5年发表了一个新版本的a w k,它的功能比旧版本增强了不少。a w k
能够用很短的程序对文档里的资料做修改、比较、提取、打印等处理。如果使用C 或P a s c a l
等语言编写程序完成上述的任务会十分不方便而且很花费时间,所写的程序也会很大。
a w k不仅仅是一个编程语言,它还是L i n u x系统管理员和程序员的一个不可缺少的工具。
a w k语言本身十分好学,易于掌握,并且特别的灵活。
gawk 是G N U计划下所做的a w k,gawk 最初在1 9 8 6年完成,之后不断地被改进、更新。
gawk 包含awk 的所有功能。
6.1 gawk的主要功能
gawk 的主要功能是针对文件的每一行( l i n e ),也就是每一条记录,搜寻指定的格式。当某
一行符合指定的格式时,gawk 就会在此行执行被指定的动作。gawk 依此方式自动处理输入文
件的每一行直到输入文件档案结束。
g a w k经常用在如下的几个方面:
• 根据要求选择文件的某几行,几列或部分字段以供显示输出。
• 分析文档中的某一个字出现的频率、位置等。
• 根据某一个文档的信息准备格式化输出。
• 以一个功能十分强大的方式过滤输出文档。
• 根据文档中的数值进行计算。
6.2 如何执行gawk程序
基本上有两种方法可以执行g a w k程序。
如果gawk 程序很短,则可以将gawk 直接写在命令行,如下所示:
gawk 'program' input-file1 input-file2 ...
其中program 包括一些pattern 和a c t i o n。
如果gawk 程序较长,较为方便的做法是将gawk 程序存在一个文件中,
gawk 的格式如下所示:
gawk -f program-file input-file1 input-file2 ...
gawk 程序的文件不止一个时,执行gawk 的格式如下所示:
gawk -f program-file1 -f program-file2 ... input-file1 input-file2 ...
6.3 文件、记录和字段
一般情况下,g a w k可以处理文件中的数值数据,但也可以处理字符串信息。如果数据没有
存储在文件中,可以通过管道命令和其他的重定向方法给g a w k提供输入。当然, g a w k只能处
理文本文件(A S C I I码文件)。
电话号码本就是一个g a w k可以处理的文件的简单例子。电话号码本由很多条目组成,每一
个条目都有同样的格式:姓、名、地址、电话号码。每一个条目都是按字母顺序排列。
在g a w k中,每一个这样的条目叫做一个记录。它是一个完整的数据的集合。例如,电话号
码本中的Smith John这个条目,包括他的地址和电话号码,就是一条记录。
记录中的每一项叫做一个字段。在g a w k中,字段是最基本的单位。多个记录的集合组成了
一个文件。
大多数情况下,字段之间由一个特殊的字符分开,像空格、TA B、分号等。这些字符叫做
字段分隔符。请看下面这个/ e t c / p a s s w d文件:
t p a r k e r ; t 3 6 s 6 2 h s h ; 5 0 1 ; 1 0 1 ; Tim Parker;/home/tparker;/bin/bash
etreijs;2ys639dj3h;502;101;Ed Tr e i j s ; / h o m e / e t r e i j s ; / b i n / t c s h
y c h o w ; 1 h 2 7 s j ; 5 0 3 ; 1 0 1 ; Yvonne Chow;/home/ychow;/bin/bash
你可以看出/ e t c / p a s s w d文件使用分号作为字段分隔符。/ e t c / p a s s w d文件中的每一行都包括
七个字段:用户名;口令;用户I D;工作组I D;注释; h o m e目录;启始的外壳。如果你想要
查找第六个字段,只需数过五个分号即可。
但考虑到以下电话号码本的例子,你就会发现一些问题:
Smith John 13 Wilson St. 555-1283
Smith John 2736 Artside Dr Apt 123 555-2736
Smith John 125 Westmount Cr 555-1726
虽然我们能够分辨出每个记录包括四个字段,但g a w k却无能为力。电话号码本使用空格作
为分隔符,所以g a w k认为S m i t h是第一个字段, John 是第二个字段,1 3是第三个字段,依次类
推。就g a w k而言,如果用空格作为字段分隔符的话,则第一个记录有六个字段,而第二个记
录有八个字段。
所以,我们必须找出一个更好的字段分隔符。例如,像下面一样使用斜杠作为字段分隔
符:
Smith/John/13 Wilson St./555-1283
Smith/John/2736 Artside Dr/Apt/123/555-2736
Smith/John/125 Westmount Cr/555-1726
如果你没有指定其他的字符作为字段分隔符,那么g a w k将缺省地使用空格或TA B作为字段
分隔符。
模式和动作
在g a w k语言中每一个命令都由两部分组成:一个模式( p a t t e r n)和一个相应的动作
(a c t i o n)。只要模式符合,g a w k就会执行相应的动作。其中模式部分用两个斜杠括起来,而动
作部分用一对花括号括起来。例如:
/ p a t t e r n 1 / { a c t i o n 1 }
/ p a t t e r n 2 / { a c t i o n 2 }
/ p a t t e r n 3 / { a c t i o n 3 }
所有的g a w k程序都是由这样的一对对的模式和动作组成的。其中模式或动作都能够被省
略,但是两个不能同时被省略。如果模式被省略,则对于作为输入的文件里面的每一行,动作
都会被执行。如果动作被省略,则缺省的动作被执行,既显示出所有符合模式的输入行而不做
任何的改动。
下面是一个简单的例子,因为gawk 程序很短,所以将gawk 程序直接写在外壳命令行:
gawk '/tparker/' /etc/passwd
此程序在上面提到的/ e t c / p a s s w d文件中寻找符合t p a r k e r模式的记录并显示(此例中没有动
作,所以缺省的动作被执行)。
让我们再看一个例子:
gawk '/UNIX/{print $2}' file2.data
此命令将逐行查找f i l e 2 . d a t a文件中包含U N I X的记录,并打印这些记录的第二个字段。
你也可以在一个命令中使用多个模式和动作对,例如:
gawk '/scandal/{print $1} /rumor/{print $2}' gossip_file
此命令搜索文件g o s s i p _ f i l e中包括s c a n d a l的记录,并打印第一个字段。然后再从头搜索
g o s s i p _ f i l e中包括r u m o r的记录,并打印第二个字段。
6.5 比较运算和数值运算
g a w k有很多比较运算符,下面列出重要的几个:
= = 相等
! = 不相等
> 大于
< 小于
> = 大于等于
< = 小于等于
例如:
gawk '$4 > 100' testfile
将会显示文件testfile 中那些第四个字段大于1 0 0的记录。
下表列出了g a w k中基本的数值运算符。
运算符说明示例
+ 加法运算2+6
- 减法运算6-3
* 乘法运算2*5
/ 除法运算8/4
^ 乘方运算3^2 (=9)
% 求余数9%4 (=1)
例如:
{print $3/2}
显示第三个字段被2除的结果。
在g a w k中,运算符的优先权和一般的数学运算的优先权一样。例如:
{print $1+$2*$3}
显示第二个字段和第三个字段相乘,然后和第一个字段相加的结果。
你也可以用括号改变优先次序。例如:
{print ($1+$2)*$3}
显示第一个字段和第二个字段相加,然后和第三个字段相乘的结果。
6.6 内部函数
g a w k中有各种的内部函数,现在介绍如下:
6.6.1 随机数和数学函数
sqrt(x) 求x 的平方根
sin(x) 求x 的正弦函数
cos(x) 求x 的余弦函数
a t a n 2 ( x,y) 求x / y的余切函数
log(x) 求x 的自然对数
exp(x) 求x 的e 次方
int(x) 求x 的整数部分
rand() 求0 和1之间的随机数
srand(x) 将x 设置为r a n d ( )的种子数
6.6.2 字符串的内部函数
• i n d e x ( i n,find) 在字符串in 中寻找字符串find 第一次出现的地方,返回值是字符串
find 出现在字符串in 里面的位置。如果在字符串in 里面找不到字符串f i n d,则返回值为
0。
例如:
print index("peanut"," a n " )
显示结果3。
• length(string) 求出string 有几个字符。
例如:
l e n g t h ( " a b c d e " )
显示结果5。
• m a t c h ( s t r i n g,r e g e x p ) 在字符串string 中寻找符合regexp 的最长、最靠左边的子字
符串。返回值是regexp 在string 的开始位置,即i n d e x值。match 函数将会设置系统变量
R S TA RT 等于i n d e x的值,系统变量RLENGTH 等于符合的字符个数。如果不符合,则会
设置R S TA RT 为0、RLENGTH 为- 1。
• s p r i n t f ( f o r m a t,e x p r e s s i o n 1,. . . ) 和printf 类似,但是sprintf 并不显示,而是返回字符
串。
例如:
sprintf("pi = %.2f (approx.)",2 2 / 7 )
返回的字符串为pi = 3.14 (approx.)
• s u b ( r e g e x p,r e p l a c e m e n t,t a rg e t ) 在字符串t a rget 中寻找符合regexp 的最长、最靠左的
地方,以字串replacement 代替最左边的r e g e x p。
例如:
str = "water,w a t e r,e v e r y w h e r e "
s u b ( / a t /, " i t h ",s t r )
结果字符串s t r会变成
w i t h e r,w a t e r,e v e r y w h e r e
• g s u b ( r e g e x p,r e p l a c e m e n t,t a rget) 与前面的s u b类似。在字符串t a rget 中寻找符合
r e g e x p的所有地方,以字符串replacement 代替所有的r e g e x p。
例如:
s t r = " w a t e r,w a t e r,e v e r y w h e r e "g s u b ( / a t /, " i t h ",s t r )
结果字符串s t r会变成
w i t h e r,w i t h e r,e v e r y w h e r e
• s u b s t r ( s t r i n g,s t a r t,length) 返回字符串string 的子字符串,这个子字符串的长度为
l e n g t h,从第start 个位置开始。
例如:
s u b s t r ( " w a s h i n g t o n ",5,3 )
返回值为i n g
如果没有length ,则返回的子字符串是从第start 个位置开始至结束。
例如:
s u b s t r ( " w a s h i n g t o n ",5 )
返回值为i n g t o n。
• tolower(string) 将字符串s t r i n g的大写字母改为小写字母。
例如:
tolower("MiXeD cAsE 123")
返回值为mixed case 123。
• toupper(string) 将字符串s t r i n g的小写字母改为大写字母。
例如:
toupper("MiXeD cAsE 123")
返回值为MIXED CASE 123。
6.6.3 输入输出的内部函数
• close(filename) 将输入或输出的文件filename 关闭。
• system(command) 此函数允许用户执行操作系统的指令,执行完毕后将回到g a w k程
序。
例如:
BEGIN {system("ls")}
6.7 字符串和数字
字符串就是一连串的字符,它可以被g a w k逐字地翻译。字符串用双引号括起来。数字不能
用双引号括起来,并且g a w k将它当作一个数值。例如:
gawk '$1 != "Tim" {print}' testfile
此命令将显示第一个字段和Ti m不相同的所有记录。如果命令中Ti m两边不用双引号,
g a w k将不能正确执行。
再如:
gawk '$1 == "50" {print}' testfile
此命令将显示所有第一个字段和5 0这个字符串相同的记录。g a w k不管第一字段中的数值
的大小,而只是逐字地比较。这时,字符串5 0和数值5 0并不相等。
6.8 格式化输出
我们可以让动作显示一些比较复杂的结果。例如:
gawk '$1 != "Tim" {print $1,$ 5,$ 6,$2}' testfile
将显示t e s t f i l e文件中所有第一个字段和Ti m不相同的记录的第一、第五、第六和第二个字
段。
进一步,你可以在p r i n t动作中加入字符串,例如:
gawk '$1 != "Tim" {print "The entry for ",$ 1,"is not Tim. ",$2}' testfile
p r i n t动作的每一部分用逗号隔开。
借用C语言的格式化输出指令,可以让g a w k的输出形式更为多样。这时,应该用p r i n t f而不
是p r i n t。例如:
{printf "%5s likes this language\n",$ 2 }
p r i n t f中的%5s 部分告诉gawk 如何格式化输出字符串,也就是输出5个字符长。它的值由
printf 的最后部分指出,在此是第二个字段。\ n是回车换行符。如果第二个字段中存储的是人
名,则输出结果大致如下:
Tim likes this language
G e o ff likes this language
Mike likes this language
Joe likes this language
gawk 语言支持的其他格式控制符号如下:
• c 如果是字符串,则显示第一个字符;如果是整数,则将数字以ASCII 字符的形式显示。
例如:
printf “% c”,6 5
结果将显示字母A。
• d 显示十进制的整数。
• i 显示十进制的整数。
• e 将浮点数以科学记数法的形式显示。
例如:
print “$ 4 . 3 e”,1 9 5 0
结果将显示1 . 9 5 0 e + 0 3。
• f 将数字以浮点的形式显示。
• g 将数字以科学记数法的形式或浮点的形式显示。数字的绝对值如果大于等于0 . 0 0 0 1则
以浮点的形式显示,否则以科学记数法的形式显示。
• o 显示无符号的八进制整数。
• s 显示一个字符串。
• x 显示无符号的十六进制整数。1 0至1 5以a至f表示。
• X 显示无符号的十六进制整数。1 0至1 5以A至F表示。
• % 它并不是真正的格式控制字符,% %将显示%。
当你使用这些格式控制字符时,你可以在控制字符前给出数字,以表示你将用的几位或几
个字符。例如,6 d表示一个整数有6位。再请看下面的例子:
{printf "%5s works for %5s and earns %2d an hour",$ 1,$ 2,$ 3 }
将会产生类似如下的输出:
Joe works for Mike and earns 12 an hour
当处理数据时,你可以指定数据的精确位数
{printf "%5s earns $%.2f an hour",$ 3,$ 6 }
其输出将类似于:
Joe earns $12.17 an hour
你也可以使用一些换码控制符格式化整行的输出。之所以叫做换码控制符,是因为g a w k对
这些符号有特殊的解释。下面列出常用的换码控制符:
\a 警告或响铃字符。
\b 后退一格。
\f 换页。
\n 换行。
\r 回车。
\t Ta b。
\v 垂直的t a b。
6.9 改变字段分隔符
在g a w k中,缺省的字段分隔符一般是空格符或TA B。但你可以在命令行使用- F选项改变字
符分隔符,只需在- F后面跟着你想用的分隔符即可。
gawk -F" ;"'/tparker/{print}' /etc/passwd
在此例中,你将字符分隔符设置成分号。注意: - F必须是大写的,而且必须在第一个引号
之前。
6.10 元字符
g a w k语言在格式匹配时有其特殊的规则。例如, c a t能够和记录中任何位置有这三个字符
的字段匹配。但有时你需要一些更为特殊的匹配。如果你想让c a t只和c o n c a t e n a t e匹配,则需要
在格式两端加上空格:
/ cat / {print}
再例如,你希望既和c a t又和C AT匹配,则可以使用或(|):
/ cat | CAT / {print}
在g a w k中,有几个字符有特殊意义。下面列出可以用在g a w k格式中的这些字符:
• ^ 表示字段的开始。
例如:
$3 ~ /^b/
如果第三个字段以字符b开始,则匹配。
• $ 表示字段的结束。
例如:
$3 ~ /b$/
如果第三个字段以字符b结束,则匹配。
• . 表示和任何单字符m匹配。
例如:
$3 ~ /i.m/
如果第三个字段有字符i,则匹配。
• | 表示“或”。
例如:
/ c a t | C AT/
和cat 或C AT字符匹配。
• * 表示字符的零到多次重复。
例如:
/UNI*X/
和U N X、U N I X、U N I I X、U N I I I X等匹配。
• + 表示字符的一次到多次重复。
例如:
/UNI+X/
和U N I X、U N I I X等匹配。
• \{a,b\} 表示字符a次到b次之间的重复。
例如:
/ U N I \ { 1,3 \ } X
和U N I X、U N I I X和U N I I I X匹配。
• ? 表示字符零次和一次的重复。
例如:
/UNI?X/
和UNX 和U N I X匹配。
• [] 表示字符的范围。
例如:
/I[BDG]M/
和I B M、I D M和I G M匹配
• [^] 表示不在[ ]中的字符。
例如:
/I[^DE]M/
和所有的以I开始、M结束的包括三个字符的字符串匹配,除了I D M和I E M之外。
6.11 调用gawk程序
当需要很多对模式和动作时,你可以编写一个g a w k程序(也叫做g a w k脚本)。在g a w k程序
中,你可以省略模式和动作两边的引号,因为在g a w k程序中,模式和动作从哪开始和从哪结
束时是很显然的。
你可以使用如下命令调用g a w k程序:
gawk -f script filename
此命令使g a w k对文件f i l e n a m e执行名为s c r i p t的g a w k程序。
如果你不希望使用缺省的字段分隔符,你可以在f选项后面跟着F选项指定新的字段分隔符
(当然你也可以在g a w k程序中指定),例如,使用分号作为字段分隔符:
gawk -f script -F";" filename
如果希望gawk 程序处理多个文件,则把各个文件名罗列其后:
gawk -f script filename1 filename2 filename3 ...
缺省情况下, g a w k的输出将送往屏幕。但你可以使用L i n u x的重定向命令使g a w k的输出送
往一个文件:
gawk -f script filename > save_file
6.12 BEGIN和END
有两个特殊的模式在g a w k中非常有用。B E G I N模式用来指明g a w k开始处理一个文件之前执行一些动作。B E G I N经常用来初始化数值,设置参数等。E N D模式用来在文件处理完成后
执行一些指令,一般用作总结或注释。
BEGIN 和E N D中所有要执行的指令都应该用花括号括起来。BEGIN 和E N D必须使用大写。
请看下面的例子:
BEGIN { print "Starting the process the file" }
$1 == "UNIX" {print}
$2 > 10 {printf "This line has a value of %d",$ 2 }
END { print "Finished processing the file. Bye!"}
此程序中,先显示一条信息: Starting the process the file,然后将所有第一个字段等于
U N I X的整条记录显示出来,然后再显示第二个字段大于10 的记录,最后显示信息: F i n i s h e d
processing the file. Bye!。
6.13 变量
在g a w k中,可以用等号( = )给一个变量赋值:
var1 = 10
在g a w k中,你不必事先声明变量类型。
请看下面的例子:
$1 == "Plastic" { count = count + 1 }
如果第一个字段是P l a s t i c,则c o u n t的值加1。在此之前,我们应当给c o u n t赋予过初值,一
般是在B E G I N部分。
下面是比较完整的例子:
BEGIN { count = 0 }
$5 == "UNIX" { count = count + 1 }
END { printf "%d occurrences of UNIX were found",count }
变量可以和字段和数值一起使用,所以,下面的表达式均为合法:
count = count + $6
count = $5 - 8
count = $5 + var1
变量也可以是格式的一部分,例如:
$2 > max_value {print "Max value exceeded by ",$2 - max_value}
$4 - var1 < min_value {print "Illegal value of ",$ 4 }
6.14 内置变量
g a w k语言中有几个十分有用的内置变量,现在列于下面:
NR 已经读取过的记录数。
FNR 从当前文件中读出的记录数。
F I L E N A M E 输入文件的名字。
FS 字段分隔符(缺省为空格)。
RS 记录分隔符(缺省为换行)。
OFMT 数字的输出格式(缺省为% g)。
OFS 输出字段分隔符。
ORS 输出记录分隔符。
NF 当前记录中的字段数。
如果你只处理一个文件,则NR 和FNR 的值是一样的。但如果是多个文件, N R是对所有
的文件来说的,而FNR 则只是针对当前文件而言。
例如:
NR <= 5 {print "Not enough fields in the record"}
检查记录数是否小于5,如果小于5,则显示出错信息。
F S十分有用,因为F S控制输入文件的字段分隔符。例如,在B E G I N格式中,使用如下的
命令:
F S = " : "
6.15 控制结构
6.15.1 if 表达式
if 表达式的语法如下:
if (expression){
c o m m a n d s
}
e l s e {
c o m m a n d s
}
例如:
# a simple if loop
(if ($1 == 0){
print "This cell has a value of zero"
}
else {
printf "The value is %d\n",$ 1
} )
再看下一个例子:
# a nicely formatted if loop
(if ($1 > $2){
print "The first column is larger"
}
else {
print "The second column is larger"
} )
6.15.2 while 循环
while 循环的语法如下:
while (expression){
c o m m a n d s
}
例如:
# interest calculation computes compound interest
# inputs from a file are the amount,interest_rateand years
{var = 1
while (var <= $3) {
p r i n t f ( " % f \ n ",$ 1 * ( 1 + $ 2 ) ^ v a r )
v a r + +}
}
6.15.3 for 循环
for 循环的语法如下:
for (initialization; expression; increment) {
c o m m a n d
}
例如:
# interest calculation computes compound interest
# inputs from a file are the amount,interest_rateand years
{for (var=1; var <= $3; var++) {
p r i n t f ( " % f \ n ",$ 1 * ( 1 + $ 2 ) ^ v a r )
}
}
6.15.4 next 和exit
next 指令用来告诉gawk 处理文件中的下一个记录, 而不管现在正在做什么。语法如下:
{ command1
c o m m a n d 2
c o m m a n d 3
n e x t
c o m m a n d 4
}
程序只要执行到n e x t指令,就跳到下一个记录从头执行命令。因此,本例中, c o m m a n d 4
指令永远不会被执行。
程序遇到e x i t指令后,就转到程序的末尾去执行E N D,如果有E N D的话。
6.16 数组
g a w k语言支持数组结构。数组不必事先初始化。声明一个数组的方法如下:
a r r a y n a m e [ n u m ] = v a l u e
请看下面的例子:
# reverse lines in a file
{line[NR] = $0 } # remember each line
END {var=NR # output lines in reverse order
while (var > 0){
print line[var]
v a r - -
}
}
此段程序读取一个文件的每一行,并用相反的顺序显示出来。我们使用N R作为数组的下
标来存储文件的每一条记录,然后在从最后一条记录开始,将文件逐条地显示出来。
6.17 用户自定义函数
复杂的gawk 程序常常可以使用自己定义的函数来简化。调用用户自定义函数与调用内部
函数的方法一样。函数的定义可以放在gawk 程序的任何地方。
用户自定义函数的格式如下:
function name (parameter-list) {
b o d y - o f - f u n c t i o n
}
name 是所定义的函数的名称。一个正确的函数名称可包括一序列的字母、数字、下标线
( u n d e r s c o r e s ),但是不可用数字做开头。p a r a m e t e r-list 是函数的全部参数的列表,各个参数之
间以逗点隔开。body-of-function 包含gawk 的表达式,它是函数定义里最重要的部分,它决定
函数实际要做的事情。
下面这个例子,会将每个记录的第一个字段的值的平方与第二个字段的值的平方加起来。
{print "sum =",S q u a r e S u m ( $ 1,$ 2 ) }
function SquareSum(x,y) {
s u m = x * x + y * y
return sum
}
到此,我们已经知道了g a w k的基本用法。g a w k语言十分易学好用,例如,你可以用g a w k
编写一段小程序来计算一个目录中所有文件的个数和容量。如果用其他的语言,如C语言,则
会十分的麻烦,相反,g a w k只需要几行就可以完成此工作。
6.18 几个实例
最后,再举几个g a w k的例子:
gawk '{if (NF > max) max = NF}
END {print max}'
此程序会显示所有输入行之中字段的最大个数。
gawk 'length($0) > 80'
此程序会显示出超过80 个字符的每一行。此处只有模式被列出,动作是采用缺省值显示
整个记录。
gawk 'NF > 0'
显示拥有至少一个字段的所有行。这是一个简单的方法,将一个文件里的所有空白行删除。
gawk 'BEGIN {for (i = 1; i <= 7; i++)
print int(101 * rand())}'
此程序会显示出范围是0 到100 之间的7 个随机数。
ls -l files | gawk '{x += $4}; END {print "total bytes: " x}'
此程序会显示出所有指定的文件的总字节数。
expand file | gawk '{if (x < length()) x = length()}
END {print "maximum line length is " x}'
此程序会将指定文件里最长一行的长度显示出来。expand 会将tab 改成s p a c e,所以是用
实际的右边界来做长度的比较。
gawk 'BEGIN {FS = ":"}
{print $1 | "sort"}' /etc/passwd
此程序会将所有用户的登录名称,依照字母的顺序显示出来。
gawk '{nlines++}
END {print nlines}'
此程序会将一个文件的总行数显示出来。
gawk 'END {print NR}'
此程序也会将一个文件的总行数显示出来,但是计算行数的工作由g a w k来做。
gawk '{print NR,$ 0 } '
此程序显示出文件的内容时,会在每行的最前面显示出行号,它的函数与‘ cat -n’类似。
function name (parameter-list) {
b o d y - o f - f u n c t i o n
}
name 是所定义的函数的名称。一个正确的函数名称可包括一序列的字母、数字、下标线
( u n d e r s c o r e s ),但是不可用数字做开头。p a r a m e t e r-list 是函数的全部参数的列表,各个参数之
间以逗点隔开。body-of-function 包含gawk 的表达式,它是函数定义里最重要的部分,它决定
函数实际要做的事情。
下面这个例子,会将每个记录的第一个字段的值的平方与第二个字段的值的平方加起来。
{print "sum =",S q u a r e S u m ( $ 1,$ 2 ) }
function SquareSum(x,y) {
s u m = x * x + y * y
return sum
}
到此,我们已经知道了g a w k的基本用法。g a w k语言十分易学好用,例如,你可以用g a w k
编写一段小程序来计算一个目录中所有文件的个数和容量。如果用其他的语言,如C语言,则
会十分的麻烦,相反,g a w k只需要几行就可以完成此工作。
6.18 几个实例
最后,再举几个g a w k的例子:
gawk '{if (NF > max) max = NF}
END {print max}'
此程序会显示所有输入行之中字段的最大个数。
gawk 'length($0) > 80'
此程序会显示出超过80 个字符的每一行。此处只有模式被列出,动作是采用缺省值显示
整个记录。
gawk 'NF > 0'
显示拥有至少一个字段的所有行。这是一个简单的方法,将一个文件里的所有空白行删除。
gawk 'BEGIN {for (i = 1; i <= 7; i++)
print int(101 * rand())}'
此程序会显示出范围是0 到100 之间的7 个随机数。
ls -l files | gawk '{x += $4}; END {print "total bytes: " x}'
此程序会显示出所有指定的文件的总字节数。
expand file | gawk '{if (x < length()) x = length()}
END {print "maximum line length is " x}'
此程序会将指定文件里最长一行的长度显示出来。expand 会将tab 改成s p a c e,所以是用
实际的右边界来做长度的比较。
gawk 'BEGIN {FS = ":"}
{print $1 | "sort"}' /etc/passwd
此程序会将所有用户的登录名称,依照字母的顺序显示出来。
gawk '{nlines++}
END {print nlines}'
此程序会将一个文件的总行数显示出来。
gawk 'END {print NR}'
此程序也会将一个文件的总行数显示出来,但是计算行数的工作由g a w k来做。
gawk '{print NR,$ 0 } '
此程序显示出文件的内容时,会在每行的最前面显示出行号,它的函数与‘ cat -n’类似。
|