Unix命令行下输入的命令是文本,输出也都是文本。因此,掌握Unix文本处理工具是很重要的一种能力。awk是Unix常用的文本处理工具中的一种,它是以其发明者(Aho,Weinberger和Kernighan)的名字首字符命名的,是一种基于模式匹配检查输入然后将期望的匹配结果处理后输出到屏幕的文本数据处理工具。
1、awk命令格式
awk ‘模式 {操作}’ 文件1 文件2 ……
awk命令的工作过程是这样的:对于每一个输入文件,逐行对其进行检查,如果该行和awk命令参数的‘模式’部分匹配,则对该行执行命令参数‘{操作}’部分所代表的操作。下面是一个简单的例子:
$cat awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 3 c 163.2.201.1 4 d www.google.com 5 e http://blog.csdn.net/xia7139 $awk 'NR==1{print}' awk_test.txt 1 a a,b,d,f
上面的例子中,用awk命令输出了awk_test.txt文件的第一行,其中命令的模式部分所用的NR是awk命令的内建变量,代表文件的行号。这样,便可以对所有行号为1的行进行打印输出。
2、常用的内建变量
变量 | 含义 |
NR | 当前处理行的行号 |
FS | 字段分隔,默认为空格或TAB |
$n | 当前处理行的第n个字段 |
$0 | 当前处理行的全部内容 |
3、几个例子及其输出
3.1 下面的例子都是对上文中的awk_test.txt文件的操作。
按行号操作: (1)打印文件的1-3行 $awk 'NR==1,NR==3{print}' awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 3 c 163.2.201.1 (2)打印文件的第1行和第3行 $awk 'NR==1||NR==3{print}' awk_test.txt 或者是 $awk '(NR==1)||(NR==3){print}' awk_test.txt 1 a a,b,d,f 3 c 163.2.201.1 (3)只打印奇数行(偶数行) $awk '(NR%2)==1{print}' awk_test.txt 1 a a,b,d,f 3 c 163.2.201.1 5 e http://blog.csdn.net/xia7139 $awk '(NR%2)==0{print}' awk_test.txt 2 b alsdjf,apple,kdjf 4 d www.google.com 使用正则表达式: (1)打印包含2的行 $awk '/2/{print}' awk_test.txt 2 b alsdjf,apple,kdjf 3 c 163.2.201.1 (2)打印以com结尾的行 $awk '/com$/{print}' awk_test.txt 4 d www.google.com 指定分隔,输出指定字段: (1)打印第1-3行的第一个字段和第三个字段 $awk 'NR==1,NR==3{print $1,$3}' awk_test.txt 1 a,b,d,f 2 alsdjf,apple,kdjf 3 163.2.201.1 (2)指定分隔符为.,输出第二个字段为csdn的行的第三个字段和整行 $awk -F. '$2=="csdn"{print $3,$0}' awk_test.txt net/xia7139 5 e http://blog.csdn.net/xia7139 (3)指定分隔符为.,输出每行的最后一个字段 $ awk -F. '{print $NF}' awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 1 com net/xia7139
3.2 awk对文件中的行按重复次数排序
下面的文件是从数据库中导出的一些数据(一部分),但是后续发现有些字段不需要。而如果重新从数据库中导出生成的话,耗费的时间太长,这里就用到了awk命令了。
文件test.txt的内容如下,每行有三个字段,字段之间用“ ::: ”隔开:
11 ::: Thomas R. Dean ::: 54 14 ::: Johann van Rensburg ::: 1 75 ::: Arun G. Phadke ::: 13 81 ::: Tiffany M. Frazier ::: 2 84 ::: Sridhar R. Iyer ::: 1 95 ::: Leesa Murray ::: 11 96 ::: David S. Munro ::: 34 104 ::: David R. Lovell ::: 2 112 ::: Steffen Rusitschka ::: 3 161 ::: Peter Forbrig ::: 116现在想只取出第后面的两个字段去掉前面的字段:
$ awk -F ::: '{print $2,$3}' test.txt Thomas R. Dean 54 Johann van Rensburg 1 Arun G. Phadke 13 Tiffany M. Frazier 2 Sridhar R. Iyer 1 Leesa Murray 11 David S. Munro 34 David R. Lovell 2 Steffen Rusitschka 3 Peter Forbrig 116发现前面有多余的不想要的空格,一点都不优雅。原来-F指定的分隔符是要将空格转义才能生效。
$ awk -F\ :::\ '{print $2,$3}' test.txt Thomas R. Dean 54 Johann van Rensburg 1 Arun G. Phadke 13 Tiffany M. Frazier 2 Sridhar R. Iyer 1 Leesa Murray 11 David S. Munro 34 David R. Lovell 2 Steffen Rusitschka 3 Peter Forbrig 116这样就好多了,但是,现在又想将上面的两个字段还是用原来的“ ::: ”隔开。
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt Thomas R. Dean ::: 54 Johann van Rensburg ::: 1 Arun G. Phadke ::: 13 Tiffany M. Frazier ::: 2 Sridhar R. Iyer ::: 1 Leesa Murray ::: 11 David S. Munro ::: 34 David R. Lovell ::: 2 Steffen Rusitschka ::: 3 Peter Forbrig ::: 116Wow, it is beautiful!
下面如果发现有重复的话,可以进行进一步的去重。这里随意生成了一个有重复的test.txt来进行操作,其内容如下:
11 ::: Thomas R. Dean ::: 54 1411 ::: Johann van Rensburg ::: 1 106 ::: Peter Forbrig ::: 116 141 ::: Johann van Rensburg ::: 1 143 ::: Johann van Rensburg ::: 1 75 ::: Arun G. Phadke ::: 13 844 ::: Sridhar R. Iyer ::: 1 149 ::: Johann van Rensburg ::: 1 81 ::: Tiffany M. Frazier ::: 2 84 ::: Sridhar R. Iyer ::: 1 95 ::: Leesa Murray ::: 11 96 ::: David S. Munro ::: 34 104 ::: David R. Lovell ::: 2 15 ::: Johann van Rensburg ::: 1 112 ::: Steffen Rusitschka ::: 3 12 ::: Steffen Rusitschka ::: 3 161 ::: Peter Forbrig ::: 116 106 ::: Peter Forbrig ::: 116首先对awk生成的结果排序:
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort Arun G. Phadke ::: 13 David R. Lovell ::: 2 David S. Munro ::: 34 Johann van Rensburg ::: 1 Johann van Rensburg ::: 1 Johann van Rensburg ::: 1 Johann van Rensburg ::: 1 Johann van Rensburg ::: 1 Leesa Murray ::: 11 Peter Forbrig ::: 116 Peter Forbrig ::: 116 Peter Forbrig ::: 116 Sridhar R. Iyer ::: 1 Sridhar R. Iyer ::: 1 Steffen Rusitschka ::: 3 Steffen Rusitschka ::: 3 Thomas R. Dean ::: 54 Tiffany M. Frazier ::: 2然后,进行去重,之所以进行排序,是因为uniq命令只能对相邻行进行去重。
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort | uniq Arun G. Phadke ::: 13 David R. Lovell ::: 2 David S. Munro ::: 34 Johann van Rensburg ::: 1 Leesa Murray ::: 11 Peter Forbrig ::: 116 Sridhar R. Iyer ::: 1 Steffen Rusitschka ::: 3 Thomas R. Dean ::: 54 Tiffany M. Frazier ::: 2如果需要根据重复次数排序,可以用“awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort | uniq -c | sort -rn”这里sort的-n选项是指定根据每行第一个字段的数字值的大小排序,比如30比4大,如果没有-n那么就是默认字典序排,4比30大。而unique中的-c选项是指定在每行之前加一个重复次数字段。
这里为了排序用sort -n先将数字字段放到前面,然后排序,排完之后,再将数字字段放到后面(实际上,可以用更加优雅的方法,直接指定按第二个字段排序就可以,这里用的是先颠过来,然后再倒回去的方法。):
$ awk -F\ :::\ '{print $3,":::",$2}' test.txt | sort | uniq | sort -rn | awk -F\ :::\ '{print $2,":::",$1}' Peter Forbrig ::: 116 Thomas R. Dean ::: 54 David S. Munro ::: 34 Arun G. Phadke ::: 13 Leesa Murray ::: 11 Steffen Rusitschka ::: 3 Tiffany M. Frazier ::: 2 David R. Lovell ::: 2 Sridhar R. Iyer ::: 1 Johann van Rensburg ::: 1
3.3 awk命令将文本文件中的数字相加
这里有一个文本文件,其中,每行的第二个字段是一个数字,现在想要将每行的数字加起来,应该如何操作呢?下面给出awk命令的版本:
$ cat awk_sum_test.txt apple 1 google 2 sammung 3 moto 4 xiaomi 5 smartisan 6 oppo 7 huawei 8 coolpad 9 lenevo 10 $ awk '{sum+=$2}END{print sum}' awk_sum_test.txt 55 $从这个命令可以看出,awk命令的使用还是比较方便的。初次之外,要知道awk命令的语法十分复杂,上面说到的只是很少的一部分。从这个例子中的用法来看,我们也能够知道前面提到的awk命令的基本格式awk ‘模式 {操作}’ 文件1 文件2 ……中的,'模式 {操作}'单位实际上,是可以有多个的,也就是说可以是awk ‘模式 {操作}模式 {操作}... ...’ 文件1 文件2 ……。awk会逐个检查每个模式,然后对符合模式的行执行相应的操作。下面是一个例子:
$ awk '{sum+=$2}NR <5 1{print $1}END{print sum}' awk_sum_test.txt apple google sammung moto xiaomi lenevo 55 $实际上,这个例子中的问题也可以用linux shell下读取文件的方法来解决,只不过稍微有点麻烦。关于awk命令,个人认为只能是边用边学,以后遇到比较好的例子,还会贴在在这里。^_^