目录
前言
经过前面的学习,我们对 awk
已经有了基础的认知,同时也学会了使用 awk
的一些基本功能。但是,要想使用好 awk
还需要我们对 awk语言
有一个更全面的认知。本节的内容更多的偏重于理论,如果对这方面不感兴趣的小伙伴也可以跳过这一节的学习,以后有需要的时候再来阅读。
我们知道 awk
程序是一个由多个 模式–动作 语句构成的序列,在某些语句中, 模式可以不存在; 还有些语句, 动作及其包围它的花括号也可以不存在. 如果程序经过 awk
检查后没有发现语法错误, 它就会每次读取一个输入行, 读取到的每一行, 按顺序检查每一个模式。 对每一个与当前行匹配的模式, 对应的动作就会执行. 一个缺失的模式匹配每一个输入行, 因此每一个不带有模式的动作对每一个输入行都会执行. 只含有模式而没有动作的语句, 会打印每一个匹配模式的输入行。
说明
本节文章使用以下文件作为程序的输入数据,我们将使用文件 countries。 每一行都包括一个国家的名字, 面积 (以千平方英里为单位), 人口 (以百万为单位), 以及这个国家所在的大陆。在文件里, 四列数据用制表符分隔, 用一个空格分隔 。文件 countries 包含下面几行:
苏联 8649 275 Asia
加拿大 3852 25 North America
中国 3705 1032 Asia
美国 3615 237 North America
巴西 3286 134 South America
印度 1267 746 Asia
墨西哥 762 78 North America
法国 211 55 Europe
日本 144 120 Asia
德国 96 61 Europe
英国 94 56 Europe
模式–动作 语句, 以及动作内的语句通常用换行符分隔, 但是若干条语句也可以出现在同一行, 只要它们之间用分号分开即可。一个分号可以放在任何语句的末尾。
程序格式
模式–动作 语句, 以及动作内的语句通常用换行符分隔, 但是若干条语句也可以出现在同一行, 只要它们之间用分号分开即可。一个分号可以放在任何语句的末尾。动作的左花括号必须与它的模式在同一行; 而剩下的部分, 包括右花括号, 则可以出现在下面几行。空行会被忽略; 它们可以插入在语句之前或之后, 用于提高程序的可读性。空格与制表符可以出现在运算符与操作数的周围, 同样也是为了提高可读性。
注释可以出现在任意一行的末尾。一个注释以井号 (#) 开始, 以换行符结束, 正如:
{ print $1, $3 } # 这是是注释的内容
一条长语句可以分散成多行, 只要在断行处插入一个反斜杠即可:
{ print \
$1, # 国家名字
$2, # 国土面积大小
$3 } # 人口数量
模式
模式控制着动作的执行: 当模式匹配时, 相应的动作便会执行. 这一小节描述模式的 6 种类型, 以及匹配它们的条件。
1. BEGIN{ statements}
在输入被读取之前, statements 执行一次。
2. END{ statements}
当所有输入被读取完毕之后, statements 执行一次。
3. expression{ statements}
每碰到一个使 expression 为真的输入行, statements 就执行. expression 为真指的是其值非零或非空。
4. /regular expression/ { statements}
当碰到这样一个输入行时, statements 就执行: 输入行含有一段字符串, 而该字符串可以被 regular expression 匹配。
5. compound pattern { statements}
一个复合模式将表达式用 &&(AND), ||(OR), !(NOT), 以及括号组合起来; 当 com-pound pattern 为真时, statements 执行。
6. pattern1, pattern2 { statements}
一个范围模式匹配多个输入行, 这些输入行从匹配 pattern1 的行开始, 到匹配 pattern2 的行结束 (包括这两行), 对这其中的每一行执行 statements。
BEGIN 与 END 不与其他模式组合。 一个范围模式不能是其他模式的一部分. BEGIN 与 END是唯一两个不能省略动作的模式。
BEGIN 与 END
BEGIN 与 END 这两个模式不匹配任何输入行. 实际情况是, 当 awk 从输入读取数据之前,BEGIN 的语句开始执行; 当所有输入数据被读取完毕, END 的语句开始执行. 于是, BEGIN 与END 分别提供了一种控制初始化与扫尾的方式。BEGIN 与 END 不能与其他模式作组合. 如果有多个 BEGIN, 与其关联的动作会按照它们在程序中出现的顺序执行, 这种行为对多个 END 同样适用。
将表达式用作模式
awk 拥有非常丰富的用来描述数值计算的表达式,同时 awk 还有用于描述字符串操作的表达式。string 都表示一个由 0个或多个字符组成的序列。
任意一个表达式都可以用作任意一个运算符的操作数. 如果一个表达式拥有一个数值形式的值, 而运算符要求一个字符串值, 那么该数值会自动转换成字符串, 类似地, 当运算符要求一个数值时, 字符串被自动转换成数值。
字符串匹配模式
Awk 提供了一种称为 正则表达式 (regular expression) 的表示法, 它可以用来指定和匹配一个字符串。一个 字符串匹配模式 (string-matching pattern) 测试一个字符串是否包含一段可以被正则表达式匹配的子字符串。
正则表达式的匹配我们前面已经使用过,例如 /美国|日本/
可以匹配美国和日本
awk '/美国|日本/ {print $0}' countries.dat
正则匹配还有另一种形式,即显示的匹配,形式如下:
expression ~ /r/
expression !~ /r/
匹配运算符 ~ 的意思是 “被… 匹配”, !~ 的意思是 “不被… 匹配”. 当 expression 的字符串值包含一段能够被正则表达式 r 匹配的子字串时, 第一个模式被匹配; 当不存在这样的子字符串时, 第二个模式被匹配。
显示的匹配模式可以精确指定针对某一个字段的匹配:
awk ' $1 ~ /美国/ {print $0}' countries.dat # 匹配第一个字段是美国的记录
并打印
关于更多的正则表达式的内容可以查看 重学正则表达式。
复合模式
一个复合模式是一个组合了其他模式的表达式, 通过括号, 逻辑运算符 ||(OR), &&(AND), !(NOT) 进行组合. 如果表达式的值为真, 那么复合模式就匹配当前输入行:
$4 == “Asia” && $3 > 500 匹配第四个字段是 Asia 并且第三个字段值大于 500 的数据。
范围模式
一个范围模式由两个被逗号分开的模式组成, 正如
pat1, pat2
一个范围模式匹配多个输入行, 这些输入行从匹配 pat1 的行开始, 到匹配 pat2 的行结束, 包括这两行; pat2 可以与 pat1 匹配到同一行, 这时候模式的范围大小就退化到了一行。作为一个例子,
模式
/Canada/, /USA/
匹配的行从包含 Canada 的行开始, 到包含 USA 的行结束。一旦范围的第一个模式匹配到了某个输入行, 那么整个范围模式的匹配就开始了; 如果范围 33
模式的第二个模式一直都没有匹配到某个输入行, 那么范围模式会一直匹配到输入结束:
awk '/Europe/, /Africa/ {print $0}' countries.dat
法国 211 55 Europe
日本 144 120 Asia
德国 96 61 Europe
英国 94 56 Europe
在下一个例子里, 变量 FNR 表示从当前输入文件中, 到目前为止读取到的行数, 变量 FILENAME 表示当前输入文件名; 它们两个都是内建变量。
awk 'FNR == 1, FNR == 5 { print FILENAME ": " $0 }' countries.dat # 打印每一个输入文件的前 5 行
上面的程序也可以写为下面的形式:
awk 'FNR <= 5 { print FILENAME ": " $0 }' countries.dat
一个范围模式不能是其他模式的一部分。
关于模式的部分就到这里了,下节内容我们学习 awk 语言的 动作。我们下节见。