非常不错的awk实例教程,详细介绍了awk命令的语法,awk命令的字符串函数、算法函数等,awk在处理字符数据时的多个例子。
写在前面,学习linux命令的速成方法,不是熟记命令的用法与各种选项,而是在linux运维中实践此命令的参数用法,多写一些解决实际问题的shell脚本。 在最初创造 awk 时,其目的是用于文本处理,并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。
该实用工具扫描文件中的每一行,查找与命令行中所给定 内容相匹配的模式。
命令的语法:
awk '{pattern + action}' {filenames}
了解字段
为了进行演示,请查看以下保存为 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 $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
注意,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 $
二、使用模式
$ 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 $
如果分别给出两个打印语句,则会产生完全不同的结果:
$ 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 命令中使用的所有特殊字符,包括:
$ awk '{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 $
通过连续设置多项标准并用管道 (|) 符号将其分隔开,您可以一次搜索多个模式匹配:
$ 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
46022 KANE SHERYL UNKNOWN AR 46026 FERGUS SARAH MUNCIE IN 46027 BUCK SARAH MUNCIE IN $
员 工 46026 和 46027 没有住在亚利桑那州;但是他们的名字中包含所搜索的字符序列。切记,当在 AWK 中进行模式匹配时,例如 grep、sed 或者大部分其他 Linux/Unix 命令,将在记录(行)中的任何位置查找匹配,除非指定进行其他操作。为解决这一问题,必须将搜索与特定字段联系起来。通过利用代字号 (~) 以及对特定字段的说明,可以达到这一目的,如下例所示:
$ awk '$5 ~ /AR/' emp_names
46022 KANE SHERYL UNKNOWN AR $
代字号(~表示匹配). (!~表示不匹配)如果搜索序列没有出现在指定字段中,则找出与搜索序列相匹配的所有行:
$ awk '$5 !~ /AR/' 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 $ 在这种情况下,将显示第五个字段中没有 AR 的所有行 — 包括两个 Sarah 条目,这两个条目确实包含 AR,但却是在第三个字段而不是第五个字段中。
三、花括号和字段分隔符
{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 $
要 利用多组括号进行重复查找,执行第一组中的命令直到完成为止;然后处理第二组命令。如果有第三组命令,则在第二组命令完成后执行它,以此类推。在所生成的 打印输出中,有两个分隔的打印命令,因此先执行第一个命令,随后执行第二个命令,这样导致每个条目显示在两行而不是一行中。
$ 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,能够正确地读取第一条记录并按要求执行。
$ 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" CITY-STATE:
"$4", "$5" "}' 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 $
五、数学操作
$ 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
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"}'
七、输入、输出和源文件
$ sort emp_names | awk '{print $3,$2}'
类似于解释程序,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 '{ print $2 | "sort" }' emp_names
BOGUE BUCK DULANEY DURHAM FELDMAN FERGUS JUNE KANE STEEN SWIM TUTTLE WOOD $
这是输出改向的情况,必须将命令包含在引号中,而管道的名称是被执行命令的名称。
$ cat awklist
{print $3,$2} {print $4,$5," "} $ $ 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 命令提供的各种规格。
规格 说明
%c 打印单个 ASCII 字符 %d 打印十进制数 %e 打印数字的科学计数表示 %f 打印浮点表示 %g 打印 %e 或 %f;两种方式都更简短 %o 打印无符号的八进制数 s 打印 ASCII 字符串 %x 打印无符号的十六进制数 %% 打印百分号;不执行转换
可以在 % 与字符之间提供某些附加的格式化参数。这些参数进一步改进数值的打印方式:
八、处理错误
awk: syntax error near line 2
awk: bailing out near line 2
切记有两条规则可以帮助您避免出现语法错误: (责任编辑:IT) |