NF
(可以理解为Number of fields)
{print NF, $1, $NF}
NR
(可以理解为Number of rows)
{print "total pay for"}
{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }
{ printf("%-8s $%6.2f\n", $1, $2 * $3) }
awk '{ printf("%6.2f %s\n", $2 * $3, $0) }' emp.data | sort -n
打印那些报酬超过 50 的
$2 * $3 > 50 { printf("$%.2f for %s\n", $2 * $3, $1) }
打印所有第一个字段是Susie 的行:
$1 == "Susie"
通过正则表达式, 打印所有包含 Susie 的行
/Susie/
模式可以使用括号和逻辑运算符进行组合, 逻辑运算符包括 &&, ||, 和 !。
打印那些 $2 至少为 4, 或者 $3 至少为 20 的行:
$2 >= 4 || $3 >= 20
两个条件都满足的行只输出一次. 将这个程序与下面这个程序作对比, 它包含两个模式,如果某行对这两个条件都满足, 它会被打印两次:
$2 >= 4
$3 >= 20
打印不满足($2 小于 4, 并且 $3 也小于 20)的行
!($2 < 4 && $3 < 20)
真实的数据总是存在错误. 检查数据是否具有合理的值, 格式是否正确, 这种任务通常称作数据验证 (data
validation), 在这一方面 awk 是一款非常优秀的工具.
数据验证在本质上是否定: 不打印具有期望的属性的行, 而是打印可疑行. 接下来的程序使用比较模式, 将
5 条合理性测试应用到 emp.data 的每一行:
NF != 3 { print $0, "number of fields is not equal to 3" }
$2 < 3.35 { print $0, "rate is below minimum wage" }
$2 > 10 { print $0, "rate exceeds $10 per hour" }
$3 < 0 { print $0, "negative hours worked" }
$3 > 60 { print $0, "too many hours worked" }
如果数据没有错误, 就不会有输出。
特殊的模式 BEGIN 在第一个输入文件的第一行之前被匹配, END 在最后一个输入文件的最后一行被处理
之后匹配. 这个程序使用 BEGIN 打印一个标题:
BEGIN { print "NAME RATE HOURS"; print "" }
{ print }
可以在同一行放置多个语句, 语句之间用分号分开。注意 print ""
打印一个空行, 它与一个单独的 print
并
不相同, 后者打印当前行
awk
可以用来进行简单的数学或字符串计算,而且可以使用内建变量、自定义变量来计算和存储数据。
在 awk
中, 用户创建的变量不需要事先声明就可以使用。
用一个变量 emp 计算工作时长超过 15 个小时的员工人数:
$3 > 15 { emp = emp + 1 }
END { print emp, "employees worked more than 15 hours" }
利用 NR 来计算平均报酬:
{ pay = pay + $2 * $3 }
END { print NR, "employees"
print "total pay is", pay
print "average pay is", pay / NR
}
很明显, printf 可以用来产生更加美观的输出. 这个程序有一个潜在的错误: 一种不常见的情况是 NR 的值为
0, 程序会尝试将 0 作除数, 此时 awk
就会产生一条错误消息。
可以通过旧字符串的组合来生成一个新字符串; 这个操作叫作拼接 (concatenation).
{ names = names $1 " " }
END { print names }
虽然在 END 动作里, NR 的值被保留了下来, 但是 $0 却不会。
打印文件最后一行
{ last = $0 }
END { print last }
awk 也提供用来计算其他值的内建函数. 求平方根, 取对数, 随机数, 除了这些数学函数, 还有其他用来操作文本的函数. 其中之一是 length
, 它用来计算字符串中字符的个数.
计算每一个人的名字的长度:
{ print $1, length($1) }
使用 length, NF 与 NR 计算行, 单词与字符的数量, 为方便起见, 我们将每个字段都当成一个单词
{ nc = nc + length($0) + 1
nw = nw + NF
}
END { print NR, "lines,", nw, "words,", nc, "characters" }
Awk 提供了用于决策的 if-else 语句, 以及循环语句, 所有的这些都来源于 C 语言. 它们只能用在动作(Action) 里。
$2 > 6 { n = n + 1; pay = 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 $6/hour"
}
一个 while 含有一个条件判断与一个循环体. 当条件为真时, 循环体执行。
{ i = 1
while (i <= $3) {
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
i = i + 1
}
}
大多数循环都包括初始化, 测试, 增值, 而 for 语句将这三者压缩成一行。
{ for (i = 1; i <= $3; i = i + 1)
printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}
一个简单的例子。
下面这个程序按行逆序显示输入数据.。第一个动作将输入行放入数组 line
的下一个元素中;
也就是说, 第一行放入 line[1]
, 第二行放入 line[2]
, 依次类推.。
END
动作用一个 while
循环, 从数组的最后一个元素开始打印, 一直打印到第一个元素为止。
{line[NR] = $0}
END { i = NR
while(i > 0){
print line[i]
i = i-1
}
}
用for循环实现等价程序:
{ line[NR] = $0 } # remember each input line
END { for (i = NR; i > 0; i = i - 1)
print line[i]
}
虽然 awk
可以写出非常复杂的程序, 但是许多实用的程序并不比我们目前为止看到的复杂多少。
这里有一些小程序集合, 对读者应该会有一些参考价值. 大多数是我们已经讨论过的程序的变形。
END { print NR }
NR == 10
{ print $NF }
{ field = $NF }
END { print field }
NF>4
$NF >4
{ nf = nf + NF }
END { print nf }
/Beth/ { nlines = nlines + 1 }
END { print nlines }
$1 > max { max = $1; maxline = $0 }
END { print max, maxline }
NF > 0
length($0) > 80
{ print NF, $0 }
{print $2, $1}
{ temp = $1; $1 = $2; $2 = temp; print}
{ $1 = NR; print }
{ $2 = ""; print }
{ for(i=NF; i>0; i=i-1)
printf("%s ", $i)
printf("\n")
}
{ sum = 0
for(i=1; i<= NF; i=i+1) sum = sum + $i
print sum
}
{ for (i = 1; i <= NF; i = i + 1) sum = sum + $i }
END { print sum }
{ for (i = 1; i <= NF; i = i + 1) if ($i < 0) $i = -$i
print
}