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~$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