目录
- 输入
- 输入分隔符
- 多行记录
- getline 函数
- 命令行变量赋值
- 命令行参数
- 与其他程序的交互
- system 函数
- 用 AWK 制作 Shell 命令
输入
为 awk 提供输入数据有若干种方式. 最常见的是把输入数据放在一个文件中。如果没有指定输入文件, awk 就从它的标准输入读取数据; 所以, 另一种常用的方法是把另一个程序的输出以管道的方式输送给 awk. 举例来说, 实用程序 egrep 从输入行中挑选具有指定正则表达式的行, 虽然 awk 也可以做同样的工作, 但是与前者相比就慢得多了. 我们可以输入命令
egrep ‘Asia’ countries | awk ‘program’
egrep 挑出那些含有 Asia 的行, 再把这些行输送给 awk 做进一步的处理。
输入分隔符
内建变量 FS 的默认值是 " ", 也就是一个空格符。把一个字符串赋值给内建变量 FS 就可以改变字段分隔符. 如果字符串的长度多于一个字符, 那么它会被当成一个正则表达式。
多行记录
默认情况下, 记录之间由换行符分隔, 所以术语 “行” 与 “记录” 在通常情况下是等价的。默认的记录分隔符可以通过向内建变量 RS 赋予新值来改变, 但是必须按照某种受限的方式来进行。如果 RS 被设置成空值正如
BEGIN { RS = “” }
那么记录之间将由一个或多个的空行来分隔, 并且每个记录可以占据多行. 把 RS 重新设置成换行符 (RS = “\n”) 就可以恢复原来的效果. 当记录由多行组成时, 无论 FS 是什么值, 换行符总是字段分隔符之一。
getline 函数
函数 getline 可以从当前输入行, 或文件, 或管道, 读取输入。getline 抓取下一个记录,按照通常的方式把记录分割成一个个的字段.。它会设置 NF, NR, 和 FNR; 如果存在一个记录, 返回1, 若遇到文件末尾, 返回 0, 发生错误时返回 -1 (例如打开文件失败)。
表达式 getline x 读取下一条记录到变量 x 中, 并递增 NR 与 FNR, 不会对记录进行分割,所以不会设置 NF。
表达式
getline <“file”
从文件 file 读取输入. 它不会对 NR 与 FNR 产生影响, 但是会执行字段分割, 并且设置 NF。
表达式
getline x <“file”
从 file 读取下一条记录, 存到变量 x 中. 记录不会被分割成字段, 变量 NF, NR, 与 FNR 都不会被修改。
命令行变量赋值
正如我们之前看到过的那样, awk 命令行具有多种形式:
awk ‘program’ f1 f2 …
awk -f progfile f1 f2 …
awk -Fsep ‘program’ f1 f2 …
awk -Fsep -f progfile f1 f2 …
在上面的命令行中, f1, f2 等变量是命令行参数, 通常代表文件名。如果一个文件名具有形式var=text, 那这就表示赋值语句, 把 text 赋值给 var, 当这个参数被当作文件来访问时, 执行赋值动作。 这种类型的赋值语句允许程序在读文件之前或之后改变变量的值。
命令行参数
命令行参数可以通常 awk 的内建数组 ARGV 来访问, 内建变量 ARGC 的值是参数的个数再加 1。 对于命令行
awk -f progfile a v=1 b
ARGC 的值是 4, ARGV[0] 含有 awk, ARGV[1] 含有 a, ARGV[2] 含有 v=1, ARGV[3] 含有 b。ARGC 之所以会比参数的个数多 1, 是因为命令的名字 awk 也被当作参数之一, 存放在索引为 0的位置, 就像 C 程序那样. 然而, 如果 awk 程序在命令行中出现, 那它就不会被当作参数, 对 -f filename 或任意的 -F 选项同样如此. 例如, 对于命令行
awk -F’\t’ ‘$3 > 100’ countries
ARGC 的值是 2, ARGV[1] 的值是 countries。
与其他程序的交互
本节主要针对Unix系统,awk 程序与其它命令的合作。
system 函数
内建函数 system(expression) 用于执行命令, 命令由 expression 给出, system 的返回值就是命令的退出状态。
用 AWK 制作 Shell 命令
到目前为止的所有例子, awk 程序都是写在一个文件中, 然后利用 -f 选项读取, 或者是出现在命令行中, 用一对单引号括起来, 就像:
awk ‘{ print $1 }’ …
Awk 用到了许多 Shell 也同样会用到的字符, 例如 $ 与 ", 把 awk 程序包围在一对单引号中可以确保 Shell 把程序原封不动地传递给 awk。
上面提到的两种执行 awk 程序的方法都要求用户自己打一些字。为了降低打字的工作量, 我们想要把命令与程序都写到一个可执行文件中, 通过键入文件名来执行程序。 假设我们想要写一个命令 field1, 用于打印每个输入行的第一个字段, 其实这非常容易, 我们把
awk ‘{ print $1 }’ $*
写到一个文件 field1 中,现在, 如果我们想要打印某些文件的每行的第 1 个字段, 只要键入
field1 filenames …
现在我们考虑实现一个更加通用的 field, 程序可以从每个输入行中打印任意组合的字段,换句话说, 命令
field n1 n2 … file1 file 2 …
按照特定的顺序打印特定的字段. 但是我们如何获取 ni 的值, 又如何区分 ni 与文件名参数?对于采用 Shell 编程的人来说, 解决上面提到的两个问题有多种方式, 如果使用 awk 的话,最简单的方式是扫描内建数组 ARGV, 获取 ni 的值之后, 把数组中对应的位置清零, 这样它们就不会被当成文件名:
BEGIN {
for (i = 1; ARGV[i] ~ /^[0-9]+$/; i++) { # collect numbers
fld[++nf] = ARGV[i]
ARGV[i] = ""
}
if (i >= ARGC) # no file names so force stdin
ARGV[ARGC++] = "-"
}
{ for (i = 1; i <= nf; i++)
printf("%s%s", $fld[i], i < nf ? " " : "\n")
}
' $*