文本处理之 awk

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

文本处理之 awk

您将会见到 /etc/passwd 文件的内容出现在眼前。现在,解释 awk 做了些什么。调用 awk 时,我们指定 /etc/passwd 作为输入文件。

然后 没有指定pattern,所以是awk                 '{action }'   第三种情况,则会针对每条记录,执行行为{print}

所得到的结果与与执行cat  /etc/passwd完全相同。

文本处理之 awk

2 在命令行输入以下命令:awk -F: '{print $1,$2,$3}' /etc/passwd | head -3

文本处理之 awk

默认的情况下,awk会将文本中的一行视为一个记录,而将一行中一个或多个非空白字符组成的单词视为字段,也就是不指定-F 参数默认就是用空格来分割字段。 现在指定: 作为分隔符。

这里通过$1引用第一人字段,类似地$2表示第二个字段,$3表示第三个字段.... $0则表示整个记录。内置变量NF记录着字段的个数,所以$NF表示最后一个字段

3 命令行输入:awk -F: '{print $NF}' /etc/passwd | head -3

文本处理之 awk

同理 $(NF-1)表示倒数第二个字段:

文本处理之 awk

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”。

文本处理之 awk



模式(Pattern)

模式是awk中比较重要的一部分,它有以下几种情况:
  • /regular expression/: 扩展的正则表达式(Extended Regular Expression),
  • relational expression: 关系表达式,例如大于、小于、等于,关系表达式结果为true表示匹配;
  • BEGIN: 特殊的模式,在第一个记录处理之前被执行,常用于初始化语句的执行;
  • END: 特殊的模式,在最后一个记录处理之前被执行,常用于输出汇总信息;
  • pattern, pattern:模式对,匹配两者之间的所有记录,类似sed的地址对;

题外话

 Linux Shell环境下提供了两种正则表达式规则,一个是基本正则表达式(BRE),另一个是扩展正则表达式(ERE)。

下面的列表给出了Linux Shell中常用的工具或命令分别支持的正则表达式的类型。

 文本处理之 awk

至于2种的差异,执行去百度

1 之前的例子都是没有模式的,awk  '{action }'     针对每条记录,执行行为

文本处理之 awk

实际应用,awk 都是要加入模式的,例如查找匹配数字3的行 seq 1 20 | awk '/3/ {print}'

文本处理之 awk

回到之前的:awk  'pattern    {action }'    如果模式匹配,则执行行为。

这里/3/是模式{print} 是行为

2 相反地,可以在在正则表达式之前加上'!'表示不匹配:seq 1 5 | awk '!/3/ {print}'

文本处理之 awk

模式可以用表达式!,&&,||   分别表示逻辑  非,与,或。

如:seq 1 20 | awk '/2/ || /4/ {print}'   

文本处理之 awk

表示匹配2,或4 。

如:seq 1 25 | awk '/1/ && /2/ {print}'  

文本处理之 awk

匹配1并且2。

3   输入: echo "ABC,DEF" | awk -F, '/ABC/{print $1,$2,0};/DEF/{print$0,1}'

文本处理之 awk

用“;”隔开两个模式和行为;两个模式都能匹配,产生了两条不同的输出,说明两个行为都被执行了。

同理:

文本处理之 awk

4  匹配某第一行:seq 1 20 | awk  'NR==1{print $0,"a"}'

文本处理之 awk

匹配第1到8行:seq 1 20 | awk  'NR==1,NR==8{print $0,"a"}'

文本处理之 awk

在awk的模式中还可以书写范围表达式。这是一种以逗号“,”隔开的两个表达式。它的含义是当第一个表达式匹配后,后续的输入也被认为匹配,直到第二个表达式匹配为止。

当然也可以用这个实现同样的效果:seq 1 20 | awk  'NR>=1&&NR<=8{print $0,"a"}'

文本处理之 awk

大家可以想想如果写成:seq 1 20 | awk  'NR>=1,NR<=8{print $0,"a"}'  是什么结果。

5  测试文件如下:

文本处理之 awk

要找字段数大于1的结果:

文本处理之 awk


6  OFS 输出时字段的分隔符,默认为空白 ,如:

文本处理之 awk

字段直接分隔符是空白,如果想用“,”分隔,你可以这么写:

echo  a b c d | awk  'NR>0{ print $1","$2","$3","$4}'

文本处理之 awk

这个比较复杂了,我们可以通过设置OFS值来做:

文本处理之 awk

通过设置OFS=",",来改变字段分隔符。

如果设置OFS为换行符“/n”,则会出现这个效果

文本处理之 awk

同理,如果设置ORS=",",输出换行符等于“,”则会出现:

文本处理之 awk