awk对于大部分人都是相当的陌生。即便一个对Linux比较熟悉的人,也未必知道awk。为什呢?因为awk与其它大多数Linux命令不同,无法从名字上看出它到底是干什么的。实际上,awk是它的三个作者姓氏的首字母合写,他们是:Aho(阿尔佛雷德·艾侯)、Winberger(彼得·温伯格)和Kernighan(布莱恩·柯林汉),绝对都是牛人。
awk是 一种文本处理工具,它的目的是编写小巧 但充满表达力的程序,把文本的输入变换为文本的输出。现今的Linux发行版所附带的awk实际上很新,是GNU的重写版本,也叫GNU awk,程序名是gawk。
使用方法
awk微型语言的解释器是awk命令。在Linux中awk实际上是gawk的一个符号连接。为了兼容性,请在任何时候都使用awk这个命令。
awk的命令格式为:
awk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
参数“-F fs”允许awk更改其字段分隔符;参数“-v var=value”可以指定变量“var”的初始值为“value”;参数“-fprogfile”为指定程序文件,而“'prog'”则为直接的程序 代码,它们可以同时存在;参数“file”就是awk的输入文件了,允许有多个。
awk程序的核心思想是模式(pattern)/行为(action)对儿这个概念,也叫模式驱动编程。模式一般是关系或正则表达式,用于与输入的每条记录进行匹配;而行为则是对模式匹配到的记录的处理方法,采用与C类似的语法,并由一对大括号“{}”括起来。
模式或行为可以省略其中一个。如果省略模式,则行为将被应用到每条输入记录;如果省略行为,则默认操作是在标准输出上打印匹配到的记录。所以,awk程序的基本结构有以下三种情况:
awk 'pattern {action }' 如果模式匹配,则执行行为
awk 'pattern ' 如果模式匹配,则在标准输出上打印记录
awk ' {action }' 针对每条记录,执行行为。
第一个 awk
1 让我们继续,开始使用 awk,以了解其工作原理。在命令行中输入以下命令:
$ awk '{ print }' /etc/passwd
您将会见到 /etc/passwd 文件的内容出现在眼前。现在,解释 awk 做了些什么。调用 awk 时,我们指定 /etc/passwd 作为输入文件。
然后 没有指定pattern,所以是awk '{action }' 第三种情况,则会针对每条记录,执行行为{print}
所得到的结果与与执行cat /etc/passwd完全相同。
2 在命令行输入以下命令:awk -F: '{print $1,$2,$3}' /etc/passwd | head -3
默认的情况下,awk会将文本中的一行视为一个记录,而将一行中一个或多个非空白字符组成的单词视为字段,也就是不指定-F 参数默认就是用空格来分割字段。 现在指定: 作为分隔符。
这里通过$1引用第一人字段,类似地$2表示第二个字段,$3表示第三个字段.... $0则表示整个记录。内置变量NF记录着字段的个数,所以$NF表示最后一个字段
3 命令行输入:awk -F: '{print $NF}' /etc/passwd | head -3
同理 $(NF-1)表示倒数第二个字段:
NF =7 表示总共七个字段。
awk提供了很多的内置变量,都非常有用。除了前面说的$0,$1,$2,...$NF 等,还有如下:
变量名
说明
FILENAME
当前输入文件的名称。如果是来自标准输入,则为空字符串。
$0
当前记录的内容
$1、$2、$3…… $n
当前记录内的字段,最大值n为内置变量NF的值。
FS
字段分隔符,正则表达式描述,默认为空格“ ”。
RS
输入记录分隔符,默认为“\n”。
NF
当前记录的字段数。
NR
已经读入的记录数。
FNR
当前输入文件的记录数。注意与NR的不同,它会针对不同的输入文件重新计数。
OFS
输出字段分隔符,默认为空格“ ”。
ORS
输出记录分隔符,默认为“\n”。
模式(Pattern)
模式是awk中比较重要的一部分,它有以下几种情况:
/regular expression/: 扩展的正则表达式(Extended Regular Expression),
relational expression: 关系表达式,例如大于、小于、等于,关系表达式结果为true表示匹配;
BEGIN: 特殊的模式,在第一个记录处理之前被执行,常用于初始化语句的执行;
END: 特殊的模式,在最后一个记录处理之前被执行,常用于输出汇总信息;
pattern, pattern:模式对,匹配两者之间的所有记录,类似sed的地址对;
【题外话:
Linux Shell环境下提供了两种正则表达式规则,一个是基本正则表达式(BRE),另一个是扩展正则表达式(ERE)。
下面的列表给出了Linux Shell中常用的工具或命令分别支持的正则表达式的类型。
至于2种的差异,执行去百度】
1 之前的例子都是没有模式的,awk '{action }' 针对每条记录,执行行为
实际应用,awk 都是要加入模式的,例如查找匹配数字3的行 :seq 1 20 | awk '/3/ {print}'
回到之前的:awk 'pattern {action }' 如果模式匹配,则执行行为。
这里/3/是模式, {print} 是行为
2 相反地,可以在在正则表达式之前加上'!'表示不匹配:seq 1 5 | awk '!/3/ {print}'
模式可以用表达式!,&&,|| 分别表示逻辑 非,与,或。
如:seq 1 20 | awk '/2/ || /4/ {print}'
表示匹配2,或4 。
如:seq 1 25 | awk '/1/ && /2/ {print}'
匹配1并且2。
3 输入: echo "ABC,DEF" | awk -F, '/ABC/{print $1,$2,0};/DEF/{print$0,1}'
用“;”隔开两个模式和行为;两个模式都能匹配,产生了两条不同的输出,说明两个行为都被执行了。
同理:
4 匹配某第一行:seq 1 20 | awk 'NR==1{print $0,"a"}'
匹配第1到8行:seq 1 20 | awk 'NR==1,NR==8{print $0,"a"}'
在awk的模式中还可以书写范围表达式。这是一种以逗号“,”隔开的两个表达式。它的含义是当第一个表达式匹配后,后续的输入也被认为匹配,直到第二个表达式匹配为止。
当然也可以用这个实现同样的效果:seq 1 20 | awk 'NR>=1&&NR<=8{print $0,"a"}'
大家可以想想如果写成:seq 1 20 | awk 'NR>=1,NR<=8{print $0,"a"}' 是什么结果。
5 测试文件如下:
要找字段数大于1的结果:
6 OFS 输出时字段的分隔符,默认为空白 ,如:
字段直接分隔符是空白,如果想用“,”分隔,你可以这么写:
echo a b c d | awk 'NR>0{ print $1","$2","$3","$4}'
这个比较复杂了,我们可以通过设置OFS值来做:
通过设置OFS=",",来改变字段分隔符。
如果设置OFS为换行符“/n”,则会出现这个效果
同理,如果设置ORS=",",输出换行符等于“,”则会出现: