Awk提供了if-else语句来做决策控制,还提供了一些语句来做循环,这些都是从C语言借来的,只能用在动作里面。
下面的程序用来计算时薪超过30美元的员工的总收入和平均收入,里面使用了 if 来防止计算平均收入时的除0错误。
$2 > 30 { n++; pay += $2 * $3 }
END { if (n > 0)
print n, "high-pay employees, total pay is", pay,
" average pay is", pay/n
else
print "No employees are paid more than $30/hour"
}
用emp.data做输入,输出为
No employees are paid more than $30/hour
在这if-else语句中,先对if后面的条件求值。如果为真,则执行第一个print语句,否则执行第二个print语句。注意我们用逗号将一个长语句分成了几行。
另外还要注意,如果if语句值控制单条语句,则不需要大括号,但如果是多条语句,就需要加上大括号了。下面这个版本对if和else控制的部分都加上了大括号,使它们的控制范围看起来更清晰。通常来说,这样的冗余是一种好的风格。
$2 > 30 { n++; pay += $2 * $3 }
END { if (n > 0) {
print n, "employees, total pay is", pay,
" average pay is", pay/n
} else {
print "No employees are paid more than $30/hour"
}
}
while语句包含一个条件和一个主体。当条件为真时,主体里面的语句会被重复执行。下面的程序用于计算一笔投资,在某个收益率的情况下,其价值在若干年内如何增长。其中使用了公式 value = amount (1 + rate)years。
# interest1 - compute compound interest
# input: amount rate years
# output: compounded value at the end of each year
{ i = 1
while (i <= $3) {
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
i++
}
}
在这个例子里,条件就是 while 后面括号内的表达式;而主体由条件后面大括号内的两条语句组成。printf 语句里,规格字符串中的 \t 表示 tab制表符, 而 ^ 是幂运算符。从 # 井号开始到行尾的文本属于注释。Awk程序会忽略注释,但注释是给程序的读者看的,用于帮助理解程序。
可以给这个程序输入三个数字,分别代表投资金额、收益率和投资年份,看看不同的值会得什么结果。比如下面展示了1000美元分别在5%和10%收益率的情况下,五年内的增长情况。用户输入为红色字体:
$ awk -f interest1.awk
1000 .05 5
1050.00
1102.50
1157.63
1215.51
1276.28
1000 .10 5
1100.00
1210.00
1331.00
1464.10
1610.51
另一个循环语句,for语句,将大部分循环所需的“初始化、条件测试、递增”这三部分压缩到一行里面。当然 for 也是从C语言借来的。可以用 for 来重写上面while的例子:
# interest2 - compute compound interest
# input: amount rate years
# output: compounded value at the end of each year
{ for (i = 1; i <= $3; i++)
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}
初始化部分即 i = 1只会执行一次。接下来是测试 i <= $3 这个条件;如果为真,则执行主体部分,即单条printf语句。主体之后再执行 i++ 递增,而循环的下一个迭代继续从条件测试开始。(条件——主体——递增)。这个代码更加紧凑,而且由于主体只有单条语句,可以不加大括号。
如果要做N次循环,从1到N(包含N),上面例子里给出的是标准用法。如果你自己写for循环语句时,发现初始化和结束条件和这个不太一样(i=1;i<=n;++i),建议再多看两眼,防止出错。
学会了条件和循环,下面来写一个有趣的程序FizzBuzz。FizzBuzz有时用来检查一个求职者是否根本不会写程序。任务很简单,就是把1到100的数字打印出来,但如果数字能被3整除,则打印fizz,如果能被5整除,则打印buzz,若都能整除,则打印fizzbuzz。
这个程序使用了取模或者说求余运算符 %
awk '
BEGIN {
for (i = 1; i <= 100; i++) {
if (i%15 == 0) # divisible by both 3 and 5
print i, "fizzbuzz"
else if (i%5 == 0)
print i, "buzz"
else if (i%3 == 0)
print i, "fizz"
else
print i
}
} '
所有事情都在BEGIN里面干了,任何文件名参数都会被忽略。注意 else if 的缩进都是一样的,表明这是同一序列的决策分支。
Awk提供了数组,用来保存一组相关的值。下面这个程序会把输入的行按行号倒序输出。
# reverse - print input in reverse order by line
{ line[NR] = $0 } # remember each input line
END { i = NR # print lines in reverse order
while (i > 0) {
print line[i]
i--
}
}
第一个动作将输入行逐一保存到数组 line 中,第一行保存在line[1],第二行保存在line[2],以此类推。END 动作使用while循环把数组内容倒序输出。
用emp.data做输入,输出是
Susie 17 18
Mary 22.50 22
Mark 25 20
Kathy 15.50 10
Dan 19 0
Beth 21 0
可以用 for 语句来重写这个程序:
# reverse - print input in reverse order by line (version 2)
{ line[NR] = $0 } # remember each input line
END { for (i = NR; i > 0; i--)
print line[i]
}
这个例子里面的数组下标是数值,但Awk最有用的一个特性就是,数组下标并不局限于数值,它可以是字符串。后续章节会有样例。