本章的后续部分,包含了一系列简单但典型的Awk程序样例,都是对前面的emp.data文件进行操作。为防遗忘,这里把emp.data文件再发一遍:
Beth 21 0
Dan 19 0
Kathy 15.50 10
Mark 25 20
Mary 22.50 22
Susie 17 18
后面的样例主要是为了向读者展示Awk能轻松地完成哪些操作——打印域、选择输出、以及数据转换。所以不要认为这些样例就包含了Awk各方面的全部能力,我们也只会对样例做简单解释,而不会详细描述其中的细节。不过到了本章结尾,你应该能有不少收获,后面的章节读起来也会更轻松。
这里我们也做个简化,不给出整个命令行,只给出程序。你可以用上一节教的两种方法之一来执行(放在单引号内或者放文件里面用-f参数)。
Awk里面有两种数据类型:数字和字符串。emp.data 文件很典型——一系列混杂着单词和数字的文本行,中间用空格或tab分隔。
Awk每次从输入中读入一行,并将其分割成域(field)。默认情况下,域是不包含任何空格或tab的字符序列。当前行的第一个域叫做 $1,第二个域是$2,以此类推。整行叫做 $0。每行的域的个数不一定是相同的。
通常我们需要做的是打印每行的几个或者全部域,可能还要做些计算。本节的程序都是这种形式的。
如果动作不带样式,则动作就是打印所有输入行。print 语句会打印出当前行。因此
{ print }
会把所有输入行打印到标准输出。而由于 $0 代表所有域,因此下面的程序也有同样的效果:
{ print $0 }
单个 print 语句能够在一个输出行中输出多个项。打印每行的第一和第三个域的程序是
{ print $1, $3 }
用emp.data做输入,会得到
Beth 0
Dan 0
Kathy 10
Mark 20
Mary 22
Susie 18
默认情况下,如果 print 语句后面的各表达式之间用逗号分隔,则输出结果的各项之间会用单个空格分隔。而且默认每个print 语句会在打印的每行末尾加入换行符。这两个默认行为都可以修改,后面的例子会有,等不及的话可以参考附录 A.4.2 部分。
你可能会觉得 $ 符号只能加个常量编号用来表示域,比如 $1, $2 等等。但实际上任意表达式都能放到 $后面来表示域的编号;Awk会对表达式求值并将其数值作为域的编号。比如,Awk有个内置的变量叫做 NF,它保存了当前输入行中域的个数。因此如下程序
{ print NF, $1, $NF }
会输出三项:域的个数,第一个域和最后一个域。
你可以对域的值进行计算并将它输出。典型的程序如下:
{ print $1, $2 * $3 }
它将每个员工的姓名和收入(时薪乘以工作时长)打印出来。后面我们会让输出好看点。
Beth 0
Dan 0
Kathy 155
Mark 500
Mary 495
Susie 306
Awk提供了一个内置变量叫做 NR,它计算到目前为止的行数(记录数)。这样我们就能用 NR 和 $0 来分别表示当前行号和当前行的内容:
{ print NR, $0 }
如果输入是emp.data,输出就是
1 Beth 21 0
2 Dan 19 0
3 Kathy 15.50 10
4 Mark 25 20
5 Mary 22.50 22
6 Susie 17 18
通过在输出列表中加入用双引号包含的字符串,可以在域或值之间输出文本(联想:SQL语句也支持类型机制)。比如
{ print "total pay for", $1, "is", $2 * $3 }
结果是
total pay for Beth is 0
total pay for Dan is 0
total pay for Kathy is 155
total pay for Mark is 500
total pay for Mary is 495
total pay for Susie is 306
在上面的 print 语句中,打印了这三种内容:双引号内的文本,输出域,计算出来的值。