Awk命令笔记

awk是一种样式扫描与处理工具。但其功能却大大强于sed和grep。awk提供了极其强大的功能,它几乎可以完成grep和sed所能完成的全部工作, 同时,它还可以可以进行样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。 它具备了一个完整的语言所应具有的几乎所有精美特性。实际上,awk的确拥有自己的语言:样式扫描和处理语言。 它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。

简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

基本用法

netstat -tnlp > netstat.txt命令中提取了如下信息作为用例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:8778            0.0.0.0:*               LISTEN      2689/python
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      2265/mysqld
tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      18777/smbd
tcp        0      0 127.0.0.1:35437         0.0.0.0:*               LISTEN      23842/python2
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1916/nginx: master
tcp        0      0 0.0.0.0:4369            0.0.0.0:*               LISTEN      1704/epmd
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      973/sshd
tcp        0      0 127.0.0.1:58205         0.0.0.0:*               LISTEN      23681/python2
tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      18777/smbd
tcp        0      0 0.0.0.0:33443           0.0.0.0:*               LISTEN      13976/phantomjs
tcp        0      0 0.0.0.0:8008            0.0.0.0:*               LISTEN      2548/unicorn_rails
tcp        0      0 192.168.217.161:5673    0.0.0.0:*               LISTEN      951/beam.smp
tcp        0      0 0.0.0.0:25673           0.0.0.0:*               LISTEN      951/beam.smp
tcp6       0      0 :::139                  :::*                    LISTEN      18777/smbd
tcp6       0      0 :::4369                 :::*                    LISTEN      1704/epmd
tcp6       0      0 :::22                   :::*                    LISTEN      973/sshd
tcp6       0      0 :::3128                 :::*                    LISTEN      2268/(squid-1)
tcp6       0      0 :::1017                 :::*                    LISTEN      988/xnServer
tcp6       0      0 :::443                  :::*                    LISTEN      950/httpd
tcp6       0      0 :::445                  :::*                    LISTEN      18777/smbd

下面是最简单最常用的awk示例,比如要想输出第1列和第4列:

1
$ awk '{print $1, $4}' netstat.txt

我们再来看看awk的格式化输出,和C语言的printf没什么两样:

1
2
3
4
5
6
7
8
9
$ awk '{printf "%-6s %-6s %-6s %-22s %-16s %-12s\n",$1,$2,$3,$4,$5,$6}' netstat.txt
Proto  Recv-Q Send-Q Local                  Address          Foreign
tcp    0      0      0.0.0.0:8778           0.0.0.0:*        LISTEN
tcp    0      0      0.0.0.0:3306           0.0.0.0:*        LISTEN
tcp    0      0      0.0.0.0:139            0.0.0.0:*        LISTEN
tcp    0      0      127.0.0.1:35437        0.0.0.0:*        LISTEN
tcp    0      0      0.0.0.0:80             0.0.0.0:*        LISTEN
tcp    0      0      0.0.0.0:4369           0.0.0.0:*        LISTEN
...

内置变量

awk经常会使用到一些内置变量,下面是它们的含义:

内置变量含义
$0当前记录(这个变量中存放着整个行的内容)
1 1~n当前记录的第n个字段,字段间由FS分隔
FS输入字段分隔符,默认是空格或Tab
NF当前记录中的字段个数,就是有多少列
NR已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中
FNR当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS输入的记录分隔符,默认为换行符
OFS输出字段分隔符,默认也是空格
ORS输出的记录分隔符,默认为换行符
FILENAME当前输入文件的名字

过滤记录

如何过滤记录(下面过滤条件为:第三列的值为0 && 第7列包含字符串’python’)

1
2
3
4
$ awk '$3==0 && $7 ~ /python/' netstat.txt
tcp        0      0 0.0.0.0:8778            0.0.0.0:*    LISTEN      2689/python
tcp        0      0 127.0.0.1:35437         0.0.0.0:*    LISTEN      23842/python2
tcp        0      0 127.0.0.1:58205         0.0.0.0:*    LISTEN      23681/python2

其中的"==“为比较运算符,其他比较运算符:!=, >, <, >=, <=

还有”~“表示正则匹配,这个就相当强大了。

如果我们需要表头的话,我们可以引入内建变量NR:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ awk '$3==0 && $6=="LISTEN" || NR==1 ' netstat.txt
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:8778            0.0.0.0:*               LISTEN      2689/python
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      2265/mysqld
tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      18777/smbd
tcp        0      0 127.0.0.1:35437         0.0.0.0:*               LISTEN      23842/python2
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1916/nginx: master
tcp        0      0 0.0.0.0:4369            0.0.0.0:*               LISTEN      1704/epmd
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      973/sshd
tcp        0      0 127.0.0.1:58205         0.0.0.0:*               LISTEN      23681/python2
...

再加上格式化输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ awk '$3==0 && $6=="LISTEN" || NR==1 {printf "%-20s %-20s %s\n",$4,$5,$6}' netstat.txt
Local                Address              Foreign
0.0.0.0:8778         0.0.0.0:*            LISTEN
0.0.0.0:3306         0.0.0.0:*            LISTEN
0.0.0.0:139          0.0.0.0:*            LISTEN
127.0.0.1:35437      0.0.0.0:*            LISTEN
0.0.0.0:80           0.0.0.0:*            LISTEN
0.0.0.0:4369         0.0.0.0:*            LISTEN
0.0.0.0:22           0.0.0.0:*            LISTEN
...

指定分隔符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ awk 'BEGIN{FS=":"} {print $1,$3,$6}' /etc/passwd
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
sync 5 /sbin
shutdown 6 /sbin
halt 7 /sbin
mail 8 /var/spool/mail
...

上面的命令也等价于:(-F的意思就是指定分隔符)

1
$ awk -F: '{print $1,$3,$6}' /etc/passwd

注:如果你要指定多个分隔符,你可以这样来:

1
awk -F '[;:]'

再来看一个以\t作为分隔符输出的例子(下面使用了/etc/passwd文件,这个文件是以:分隔的):

1
2
3
4
5
6
7
$ awk -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd
root    0    /root
bin     1    /bin
daemon  2    /sbin
adm     3    /var/adm
lp      4    /var/spool/lpd
sync    5    /sbin

字符串匹配

1
2
3
4
5
$ awk '$7 ~ /python/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
1	Local	Address	Foreign
2	0.0.0.0:8778	0.0.0.0:*	LISTEN
5	127.0.0.1:35437	0.0.0.0:*	LISTEN
9	127.0.0.1:58205	0.0.0.0:*	LISTEN

上面示例第7列匹配"python"字符串,其实 ~ 表示模式开始。/ /中是模式,这就是一个正则表达式的匹配。

其实awk可以像grep一样的去匹配一整行,就像这样:

1
2
3
4
$ awk '/python/' netstat.txt
tcp   0      0 0.0.0.0:8778     0.0.0.0:*     LISTEN      2689/python
tcp   0      0 127.0.0.1:35437  0.0.0.0:*     LISTEN      23842/python2
tcp   0      0 127.0.0.1:58205  0.0.0.0:*     LISTEN      23681/python2

再来看看模式取反的例子:

1
awk '!/python/' netstat.txt

统计

下面的命令计算所有的.py文件和.txt文件大小总和:

1
2
$ ls -l *.py *.txt | awk '{sum+=$5} END {print sum}'
3369