awk是linux下的一个强大的文本分析处理工具,同时awk其实是一门编程语言,它支持条件判断、数组、循环等功能。awk处理数据的方式是逐行扫描文件,对符合条件的行进行处理。
awk命令的格式如下:
awk [选项] '脚本命令' 文件名
常用的选项主要有以下几种:
-F //指定输入的分隔符
-f //从脚本文件中读取awk的命令,取代从命令行输入指令
-v //设置一个变量
awk命令中的脚本命令由两个部分组成,如下:
'匹配规则{执行命令}'
这表示awk在逐行扫描文件时,只有对满足匹配规则的行才执行后续的命令,不满足匹配规则的行则不进行处理。
了解了awk的命令构成后,我们就从最简单的awk命令来学习。
例1
假设有如下文本,名为1.txt:
1 aa ccc
2 bb ddd
3 ee fff
4 gg hhh
现执行下述指令:
awk '{print}' 1.txt
对照的awk的命令格式来看上述指令,发现上面的指令没有带选项,也没有匹配规则,是非常简单的一个awk指令。执行命令中的print意思也非常明显,就是起打印的作用,1.txt则是awk扫描的文件。所以该命令的作用就是逐行扫描1.txt文件,然后打印每一行。所以该指令的输出如下:(原样输出1.txt)
1 aa ccc
2 bb ddd
3 ee fff
4 gg hhh
现在给上面的awk指令加点东西,如下:
awk '{print $1}' 1.txt
输出如下:
1
2
3
4
结果很明显,awk指令打印出了文本中的第一列。事实上,$1是awk的内置变量,表示的是当前行被分隔符分隔后的第1列。那如果要打印第一列和第二列呢?也很简单,如下:
awk '{print $1,$2}' 1.txt
这样就能把第一列和第二列的内容都打印出来了。
除了可以对文本的内容进行输出,我们也可以加上自己的字段,例如:
awk '{print $2,"666"}' 1.txt
输出如下:
aa 666
cc 666
ee 666
gg 666
awk的分隔符有两种,一种是输入分隔符,一种是输出分隔符。
输入分隔符是指在awk逐行扫描文本时,根据输入分隔符将当前行分割成若干列。从上面指令的结果可以很明显的看出awk默认的输入分隔符是空格。除了使用空格作为分隔符,awk也支持指定输入分隔符。现有如下文本:
1,aa,bbb
2,cc,ddd
3,ee,eee
假定文件名为1.txt。
在awk指令中可以使用-F的选项来设置自定义分隔符。
awk -F, '{print $1,$2}' 1.txt
输出:
1 aa
2 cc
3 ee
可见,通过-F的选项设置,awk在逐行处理文本时不再采用默认的空格而是采用逗号来作为输入分隔符。
通过观察上述所有awk处理的文本数据输出,可以看到每一行的输出数据都是用空格来分隔开来的。所以awk指令的默认输出分隔符也是空格。在awk中有一个内置变量OFS表示输出分隔符,我们可以通过设置OFS的值来设定输出分隔符。而要使用变量则需要使用的-v选项,该选项的意义就是设置变量的值。
有如下文本(1.txt):
1 aa bbb
2 cc ddd
现在我们希望awk指令输出时采用冒号进行分隔每一列数据。
awk -v OFS=":" '{print $1,$2,$3}' 1.txt
输出:
1:aa:bbb
2:cc:ddd
如上,awk的输出不再以空格来分隔每一列数据,而是以自定义的冒号来分隔数据。
那么可不可以awk在输出数据时每一列都不要分隔符呢?答案是可以的。
观察awk的执行命令中print动作后面是打印$1,$2,$3,这几个内置变量之间都是有逗号来分开的。这说明print在输出文本时也需要有分隔符来隔开每一列。所以要想输出时没有分隔符来分隔每一列也很简单,就是在print后面的列中不加逗号就可,如下:
awk '{print $1 $2 $3}' 1.txt
输出:
1aabbb
2ccddd
这样一来就明白了吧。
当然,在使用awk处理文本时也可以同时指定输入分隔符和输出分隔符。
例如:
awk -F -v OFS="*" '{print $1,$2,$3}' 1.txt
输出:
1*aa*bbb
2*cc*ddd
所谓匹配规则就是在awk指令逐行处理文本时对当前行进行判断,满足匹配规则才进行处理。大多数时候,匹配规则都由正则表达式来指定。举几个小栗子,现有如下文本:
1 aa bbb
2,cc,ddd
3 ee fff
4 gg hhh
假如现在只对第一行进行处理:
awk 'NR==1{print $0}' 1.txt
输出:
1 aa bbb
如上,NR也是awk的内置变量之一,表示当前的行号,$0表示所有列。所以上述指令是打印第一行的数据。其他行不满足NR==1的匹配规则,所以都不会执行print $0的指令。
再看如下指令:
awk '/^$/{print "blank:" NR}' 1.txt
输出:
blank:2
blank:4
上述指令中的匹配规则/^$/则是表示空行。所以awk指令只有处理到空行时才打印信息。如结果所示,我们可以很清楚的知道哪些行是空行。
awk变量分为内置变量和自定义变量两种。内置变量指的是awk已经明确定义其含义的变量,例如上面已经用到过得NR,OFS,$1等等都是内置变量。自定义变量则是用户自己定义的变量。
awk的常用内置变量主要有以下:
变量名 | 含义 |
---|---|
NR | 当前行的行号 |
NF | 当前行被分隔符分隔后的字段数量 |
FS | 输入分隔符 |
OFS | 输出分隔符 |
FNR | 各文件分别记录的行号 |
RS | 输入记录分隔符 |
ORS | 输出记录分隔符 |
FILENAME | 文件名 |
上述的常用内置变量,有些已经在上面的例子中有使用,想必大家也清楚了他们的函数和用途。接下来,在举一些例子来看一下其他几个内置变量的方法。
NF内置变量很容易理解,就是每一行被分隔成了几列。有如下文本:
a
b bb
c cc ccc
d dd
现在使用awk指令来输出每一行都被分隔成了几列:
awk '{print NF}' 1.txt
输出:
1
2
3
2
RS、ORS的内置变量和FS,OFS变量有点相似。我们知道FS,OFS是指定输入输出分隔符的,而RS,ORS则是指定记录分隔符的。什么意思呢?意思就是awk在处理数据时默认是以换行符来处理一条条记录的,输出数据时也是默认以换行符输出一条条记录的。RS,和OFS则是指定记录分隔符用的。
还是以上面的文本作为例子,awk指令为:
awk -v RS=" " {print NR,$0} 1.txt
输出:
1 a
b
2 bb
c
3 cc
4 ccc
d
5 dd
在上述指令中,我们设置记录分隔符为空格,所以awk每遇见一个空格,就会认为是一条记录。例如上面的输出中
1 a
b
事实上是awk处理的一条记录,因为a后面跟的是换行符,不是空格,所以awk会认为这不是一条记录,就会继续处理跟在换行符后面的b,处理完b后发现有一个空格,此时才结束第一条记录的处理。后面的处理也是一样的。
内置变量ORS则是设置输出记录分隔符的。感兴趣的同学们可以尝试设置输出记录分隔符为空格看下输出会是什么。
内置变量FILENAME很显然是表示文件名的。如下:
awk '{print FILENAME}' 1.txt
输出:
1.txt
1.txt
1.txt
1.txt
为什么会打印这么多文件名呢?这是因为awk在处理每一条记录时都会打印文件名。所以awk处理了多少条记录就会打印多少个文件名。
当然也支持多个文件的处理,例如:
awk '{print FILENAME}' 1.txt 2.txt
该指令在处理时会先对1.txt文件进行处理,处理完了1.txt再处理2.txt。相当于:
awk '{print FILENAME}' 1.txt
awk '{print FILENAME}' 2.txt
自定义变量有两种定义方式,一种是用-v选项来指定变量,另一种则在执行命令中直接定义。
第一种:
awk -v a="666" '{print a,$0}' 1.txt
第二种:
awk '{a="666";print a,$0}' 1.txt
两种使用方法都很简单,这里就不再举例。
awk还有两个特殊的关键字,BEGIN和END。这两个关键字也很好理解,BEGIN就是在处理文本之前所做的操作,END则是在处理文本以后所做的操作。举一个简单的例子,有如下文本:
aa aaa
bb bbb
cc ccc
awk指令如下:
awk 'BEGIN{print "begin"} {print $0} END{print "end"}' 1.txt
输出如下:
begin
aa aaa
bb bbb
cc ccc
end
一目了然吧!
通过以上介绍,我们已经掌握了awk指令的基本用法,可以看出awk指令对文本处理的能力真的是很强大。掌握awk工具的使用,我们做对于文本处理的相关工作能很大程度上提高工作的效率。