在Awk里,一个“动作”是一系列由换行或分号分隔开的语句。前面的例子给出了单个 print 或 printf语句构成的动作。本节会给出一些例子,包含简单的数值或字符串计算的语句。在这些语句中你不仅能使用内置变量如NF,还能创建你自己的变量,用来执行计算、储存数据,以及类似操作。在Awk里,用户创建的变量不需要事先声明;当你使用它们的时候,它们就应运而生了。
下面这个程序使用变量 emp 计算工作超过15小时的员工个数
$3 > 15 { emp = emp + 1 }
END { print emp, "employees worked more than 15 hours" }
每读到一行时,若该行的第三个域大于15,则会将变量 emp 递增 1。用emp.data作为输入,输出是
3 employees worked more than 15 hours
Awk里的数值变量初始值是0,因此我们不需要对emp进行初始化。下面这种语句
emp = emp + 1
在程序中出现太频繁了,因此C语言,以及很多受其启发的语言,专门为之提供了一个等价的操作符 ++ ,用来简化代码:
emp++
对应地还有个递减操作符 -- ,后面很快会提到。
因此上面的计数程序可以用 ++ 来重写:
$3 > 15 { emp++ }
END { print emp, "employees worked more than 15 hours" }
为了计算员工的个数,我们不用自己造变量,而是使用内置变量 NR,它保存着到目前为止读入的行数;当所有输入都结束之后,它的值就是读入的总行数。
END { print NR, "employees" }
的输出为
6 employees
下面这个程序使用 NR 来计算平均值
{ pay = pay + $2 * $3 }
END { print NR, "employees"
print "total pay is", pay
print "average pay is", pay/NR
}
其中第一个动作对所有员工的薪水进行累加计算。而 END 动作会输出
6 employees
total pay is 1456
average pay is 242.667
很明显这个程序不完美,可以使用 printf 来打印更整洁的输出,比如将小数点后数字控制在两位。而且还有个潜在的错误,虽然出现的情况很少:如果没读到数据,NR为0 ,程序会出现除0错误。
另外,操作符 += 也可以简化变量递增的写法,它会将表达式左边的变量加上右边的值,因此上面程序的第一行可以简写为:
{ pay += $2 * $3 }
Awk变量既能表示数字也能表示字符串。下面这个程序找出哪个员工的时薪最高:
$2 > maxrate { maxrate = $2; maxemp = $1 }
END { print "highest hourly rate:", maxrate, "for", maxemp }
输出结果是
highest hourly rate: 25 for Mark
这个程序里,变量 maxrate 存的是数值,而变量 maxemp 存的是字符串。如果多个员工有相同的时薪,这个程序只会输出第一个。(想想怎么做到全部输出?)
可以把老字符串拼在一起形成新字符串;这个操作称为“连接”。在Awk程序里做字符串连接操作,就是要将字符值一个接一个地写出来;没有单独的字符串连接操作符。(现在看来,Awk的这个设计不够理想,因为有时会导致难以定位的错误)
这个程序演示了字符串连接操作:
{ names = names $1 " " }
END { print names }
通过将每个名字和一个空格拼接到变量 names 之前保存的值后面,就得到所有员工的名字。names的值在END的动作中打印:
Beth Dan Kathy Mark Mary Susie
在每个输入行,程序的第一条语句会拼接三个值:names之前的值,第一个域,还有一个空格;然后它将这个结果赋给names。这样,当读入所有的输入行,names就包含了单个字符串,其中是所有员工的姓名,每个姓名后面带个空格(注意最后一个姓名后面也有个看不见的空格)。用于保存字符串的变量在创建时包含了空字符串(即不包含字符),因此这个程序里names也不需要明确初始化。
前面的第二个例子中,内置变量如 NR 在 END 动作时保留了它的值,类似的, $0 也一样保留了。程序
END { print $0 }
可以用来打印最后一行。输出为
Susie 17 18
我们已经看到Awk使用内置变量来维护频繁使用的值,如域的个数和输入行数。类似的,Awk提供了内置函数来计算其他有用的值。
除了平方根、对数、随机数等数学函数,还有用来操作文本的函数。比如 length,计算字符串中的字符。下面这个程序计算每个人的名字长度
{ print $1, length($1) }
结果是
Beth 4
Dan 3
Kathy 5
Mark 4
Mary 4
Susie 5
下面这个程序用了 length, NF,NR 来计算输入的行数、单词数和字符数,功能类似Unix程序 wc。为了方便,我们将每个域看做一个单词(当然这样把问题简化了)
{ nc += length($0) + 1
nw += NF
}
END { print NR, "lines,", nw, "words,", nc, "characters" }
用emp.data文件做输入,结果是
6 lines, 18 words, 77 characters
注意到我们在每个输入行的 nc 变量里面加了1,这是因为 $0 不包括换行符,我们要把它算上。