导读
相对于 sed 常常作用于一整行的处理,awk 则比较倾向于将一行分成数个“字段”来处理。
本文翻译自 simple awk tutorial
习惯了英文者,可自行前往阅读。
接下来的例子中会使用到 employee.txt 文件。
$ cat employee.txt 100 Thomas Manager Sales $5,000 200 Jason Developer Technology $5,500 300 Raj Sysadmin Technology $7,000 400 Nisha Manager Marketing $9,500 500 Randy Manager Sales $6,000
awk是做什么的?
最简单的 awk 用法是基于列的数据处理,比如说表格,使用标准输入将数据导入。变量 $1, $2...表示当前输入行的第一列,第二列...。打印出一个文件的第二列,你可能会使用下面的 awk 脚本:
awk < file '{ print $2 }'
这意味着“每一行,打印出第二个字段”
例如:
$awk < employee.txt '{ print $2 }' Thomas Jason Raj Nisha Randy
为了打印出第二列和第三列,可以
awk < file '{ print $2, $3 }'
分隔符
awk 将行分隔为字段,默认会使用空白符(whitespace)来分隔,也就是空格符(spaces)或制表符(tabs)。 你可以使用 -F 选项来指定其它字符为分隔符。例如,为了打印出当前系统中所有用户的主目录,你可以
$awk < /etc/passwd -F: '{ print $6 }'
passwd 的文件内容以冒号来分隔,主目录在第六个字段。
运算
awk是一种弱类型语言;变量既可以是字符串,也可以是数字,这取决于你怎样引用它们。所有的数字都是浮点型的。所以,为了实现华氏温度与摄氏温度之间的转换,你可以
awk '{ print ($1-32)*(5/9) }'
标准输入的华氏温度会被转换为摄氏温度,直到遇到 EOF,该 awk 程序停止。
可选的操作符与C基本一样。紧接着的两个字符串表达式(以空格分隔或没有空格),会连接。‘+’ 总是加法。
echo 5 4 | awk '{ print $1 + $2 }
会输出 “9”,然而
echo 5 4 | awk '{ print $1 $2 }'
会输出 “54”,注意
echo 5 4 | awk '{ print $1, $2 }'
会输出 “5 4”。
变量
awk 有一些内置的、自动置值的变量,比如上文提到的$1。其它的内置变量中,适合初学者的,也就是 $0 和 NF 了,前者表示当前输入行,后者表示当前输入行($0)的字段总数。$NF表示当前输入行的最后一个字段。
你可以定义自己的变量,除了 awk 保留的关键词,你可以用任何名字来引用它。变量没有明确赋值的,字符串为“”,数字为0。
例如,下面的代码会打印出每行数字的平均值:
awk '{ tot=0; for (i=1; i <= NF; i++) tot += $i; print tot/NF; }'
使用 $i 去取第 i 个变量的值, for 循环与C语言类似。 tot 在代码的开头作初始化的原因,是这个代码运行于每一个输入行,如若不,当开始运行第二行的时候, tot 将具有第一行结束时的值。
块Block
可能,你仅有一些特殊字段的数字需要单独相加。为了这样做,你需要在结束时打印结果。采用的方法为
awk '{ tot += $1; n += 1; } END { print tot/n }'
注意两个语句块的使用。第二个之前有个 END;意味着一旦所有的输入处理结果,就运行这个语句块。一般,你可以在语句块之前添加各种条件,仅当条件满足时语句块才被执行。也就是,你可以
awk '$1 == 0 { print $2 }'
仅在第一个字段为0时才打印第二个字段。你还可以使用正则表达式来匹配行数据:
awk '/^test/' { print $2 }
如果你没有添加条件,每行数据输入时都会运行块语句。
语句块的特殊条件 BEGIN 和 END,分别运行于输入数据的处理之前和处理之后。
printf
awk 包含了 printf 语句,本质上类似于C语言的 printf。当你想要格式化输出,或者将多种元素整合到一行,可以使用它。(print 暗含增加一个'\n';printf不会)。
比如,移除每一行第一字段
awk '{ for (i=2; i<=NF; i++) printf "%s ", $i; printf "\n"; }'
注意,使用NF迭代所有的字段,显示地使用 printf 输出 '\n'。