AWK是贝尔实验室1977年搞出来的文本出现神器
之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符
今天这篇文章主要目的还是为了引起大家对 awk 的兴趣,对于这个上古神器还需要大家自行去查阅相关文档去进一步学习
通常,awk 是以文件的一行为处理单位的,awk 每接受文件的一行,就会执行相应的命令来处理文本
例如:执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令
awk '{print 1 $0}' /etc/passwd
1. 过滤
从 netstat 命令中提取了以下信息作为例子
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 saltedfish.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 saltedfish.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 saltedfish.cn:80 110.194.134.189:1032 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 saltedfish.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 saltedfish.cn:80 123.169.124.111:49829 ESTABLISHED
tcp 0 0 saltedfish.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 saltedfish.cn:80 61.148.242.38:30901 ESTABLISHED
tcp 0 1 saltedfish.cn:80 124.152.181.209:26825 FIN_WAIT1
tcp 0 0 saltedfish.cn:80 110.194.134.189:4796 ESTABLISHED
tcp 0 0 saltedfish.cn:80 183.60.212.163:51082 TIME_WAIT
tcp 0 1 saltedfish.cn:80 208.115.113.92:50601 LAST_ACK
tcp 0 0 saltedfish.cn:80 123.169.124.111:49840 ESTABLISHED
tcp 0 0 saltedfish.cn:80 117.136.20.85:50025 FIN_WAIT2
tcp 0 0 :::22 :::* LISTEN
比如说我们想输出例子中的第一列和第四列
- $n:表示第几列($0表示整个内容)
- 执行的操作只能是单引号
awk '{print $1, $4}' test.txt
Proto Local-Address
tcp 0.0.0.0:3306
tcp 0.0.0.0:80
tcp 127.0.0.1:9000
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp saltedfish.cn:80
tcp :::22
加上过滤条件
1、过滤条件:第一列的值为 tcp && 第六列的值为 LISTEN
awk '$1=="tcp" && $6=="LISTEN"' test.txt
2、过滤条件:第三列的值大于等于1,打印过滤出的内容
awk '$3>=1 {print$0}' test.txt
常用的比较运算符
==:等于
!=:不等于
>:大于
<:
>=:
<=:
指定分隔符
- 默认是以空格作为分隔符
- -F 参数:指分隔符
1、以空格作为分隔符,打印出第四列内容
awk '{print $4}' test.txt
或者
awk -F ' ' '{print $4}' test.txt
Local-Address
0.0.0.0:3306
0.0.0.0:80
127.0.0.1:9000
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
saltedfish.cn:80
:::22
2、以 “:” 作为分隔符,打印出第二列内容
awk -F ':' '{print $2}' test.txt
3306 0.0.0.0
80 0.0.0.0
9000 0.0.0.0
80 124.205.5.146
80 61.140.101.185
80 110.194.134.189
80 123.169.124.111
80 116.234.127.77
80 123.169.124.111
80 183.60.215.36
80 61.148.242.38
80 124.152.181.209
80 110.194.134.189
80 183.60.212.163
80 208.115.113.92
80 123.169.124.111
80 117.136.20.85
3、如果要指定多个分隔符,可以这样:
awk -F '[;:]'
2. awk 内置变量
$0 | 当前记录(这个变量中存放着整个行的内容) |
---|---|
$1~$n | 当前记录的第n个字段,字段间由FS分隔 |
FS | 输入字段分隔符 默认是空格或Tab |
NF | 当前记录中的字段个数,就是有多少列 |
NR | 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。 |
FNR | 当前记录数,与NR不同的是,这个值会是各个文件自己的行号 |
RS | 输入的记录分隔符, 默认为换行符 |
OFS | 输出字段分隔符, 默认也是空格 |
ORS | 输出的记录分隔符,默认为换行符 |
FILENAME | 当前输入文件的名字 |
1、我们对输出结果加上行号(过滤条件:第三列的值大于1)
awk '$3>=1 {print NR,$0}' test.txt
1 $ cat netstat.txt
2 Proto Recv-Q Send-Q Local-Address Foreign-Address State
13 tcp 0 4166 saltedfish.cn:80 61.148.242.38:30901 ESTABLISHED
14 tcp 0 1 saltedfish.cn:80 124.152.181.209:26825 FIN_WAIT1
17 tcp 0 1 saltedfish.cn:80 208.115.113.92:50601 LAST_ACK
2、忽略第一行(即 NR>1)
awk '$3>=1 && NR>1 {print NR,$0}' test.txt
2 Proto Recv-Q Send-Q Local-Address Foreign-Address State
13 tcp 0 4166 saltedfish.cn:80 61.148.242.38:30901 ESTABLISHED
14 tcp 0 1 saltedfish.cn:80 124.152.181.209:26825 FIN_WAIT1
17 tcp 0 1 saltedfish.cn:80 208.115.113.92:50601 LAST_ACK
3、输出字段以 tab 分隔(以 /etc/passwd 为例)
字段分隔符默认是空格
awk -F ':' '{print $1,$3} ' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
chrony 998
tss 59
ntp 38
tcpdump 72
zabbix 997
minion2 1000
nginx 996
指定为 tab
awk -F ':' '{print $1,$3} ' OFS='\t' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
chrony 998
tss 59
ntp 38
tcpdump 72
zabbix 997
minion2 1000
nginx 996
4、输出记录以 tab 分隔(以 /etc/passwd 为例)
记录(行)分隔符默认是换行符
awk -F ':' '{print $1,$3} ' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11
games 12
ftp 14
nobody 99
systemd-network 192
dbus 81
polkitd 999
sshd 74
postfix 89
chrony 998
tss 59
ntp 38
tcpdump 72
zabbix 997
minion2 1000
nginx 996
指定为 tab
awk -F ':' '{print $1,$3} ' ORS="\t" /etc/passwd
root 0 bin 1 daemon 2 adm 3 lp 4 sync 5 shutdown 6 halt 7 mail 8 operator 11 games 12 ftp 14 nobody 99 systemd-network 192 dbus 81 polkitd 999 sshd 74 postfix 89 chrony 998 tss 59 ntp 38 tcpdump 72 zabbix 997 minion2 1000 nginx 996
3.正则
字符串匹配
~ 表示模式开始、/ /中是模式。这就是一个正则表达式的匹配
1、匹配 LISTEN
awk '$6 ~ /LISTEN/ {print $0}' test.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 :::22 :::* LISTEN
2、支持模糊匹配
匹配 LIS 的话,LISTEN 和 EATABLISTED 都会输出
awk '$6 ~ /LIS/ {print $0}' test.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 saltedfish.cn:80 110.194.134.189:1032 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49829 ESTABLISHED
tcp 0 4166 saltedfish.cn:80 61.148.242.38:30901 ESTABLISHED
tcp 0 0 saltedfish.cn:80 110.194.134.189:4796 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49840 ESTABLISHED
tcp 0 0 :::22 :::* LISTEN
3、多个匹配条件
awk '$6 ~ /LISTEN|FIN/{print $0}' test.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 saltedfish.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 saltedfish.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 1 saltedfish.cn:80 124.152.181.209:26825 FIN_WAIT1
tcp 0 0 saltedfish.cn:80 117.136.20.85:50025 FIN_WAIT2
tcp 0 0 :::22 :::* LISTEN
4、取反匹配
awk '$6 !~ /LISTEN|FIN/ {print $0}' test.txt
$ cat netstat.txt
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 saltedfish.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 saltedfish.cn:80 110.194.134.189:1032 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 saltedfish.cn:80 123.169.124.111:49829 ESTABLISHED
tcp 0 0 saltedfish.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 saltedfish.cn:80 61.148.242.38:30901 ESTABLISHED
tcp 0 0 saltedfish.cn:80 110.194.134.189:4796 ESTABLISHED
tcp 0 0 saltedfish.cn:80 183.60.212.163:51082 TIME_WAIT
tcp 0 1 saltedfish.cn:80 208.115.113.92:50601 LAST_ACK
tcp 0 0 saltedfish.cn:80 123.169.124.111:49840 ESTABLISHED
4.BEGIN & END
BEGIN 和 END 这两个关键字意味着执行前和执行后的意思,语法如下:
- BEGIN{ 这里面放的是执行前的语句 }
- END {这里面放的是处理完所有的行后要执行的语句 }
- {这里面放的是处理每一行时要执行的语句}
以下面内容为例(将 top 输出结果重定向到 top.txt 中)
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 125636 4148 ? Ss 11月11 0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0.0 0.0 0 0 ? S 11月11 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< 11月11 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 11月11 0:00 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? S 11月11 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 11月11 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? R 11月11 0:07 [rcu_sched]
root 10 0.0 0.0 0 0 ? S< 11月11 0:00 [lru-add-drain]
root 11 0.0 0.0 0 0 ? S 11月11 0:00 [watchdog/0]
root 12 0.0 0.0 0 0 ? S 11月11 0:00 [watchdog/1]
root 13 0.0 0.0 0 0 ? S 11月11 0:00 [migration/1]
root 14 0.0 0.0 0 0 ? S 11月11 0:00 [ksoftirqd/1]
root 16 0.0 0.0 0 0 ? S< 11月11 0:00 [kworker/1:0H]
root 18 0.0 0.0 0 0 ? S 11月11 0:00 [kdevtmpfs]
root 19 0.0 0.0 0 0 ? S< 11月11 0:00 [netns]
root 20 0.0 0.0 0 0 ? S 11月11 0:00 [khungtaskd]
root 21 0.0 0.0 0 0 ? S< 11月11 0:00 [writeback]
root 22 0.0 0.0 0 0 ? S< 11月11 0:00 [kintegrityd]
root 23 0.0 0.0 0 0 ? S< 11月11 0:00 [bioset]
root 24 0.0 0.0 0 0 ? S< 11月11 0:00 [bioset]
root 25 0.0 0.0 0 0 ? S< 11月11 0:00 [bioset]
root 26 0.0 0.0 0 0 ? S< 11月11 0:00 [kblockd]
root 27 0.0 0.0 0 0 ? S< 11月11 0:00 [md]
root 28 0.0 0.0 0 0 ? S< 11月11 0:00 [edac-poller]
root 29 0.0 0.0 0 0 ? S< 11月11 0:00 [watchdogd]
root 35 0.0 0.0 0 0 ? S 11月11 0:00 [kswapd0]
root 36 0.0 0.0 0 0 ? SN 11月11 0:00 [ksmd]
root 37 0.0 0.0 0 0 ? SN 11月11 0:00 [khugepaged]
root 38 0.0 0.0 0 0 ? S< 11月11 0:00 [crypto]
root 46 0.0 0.0 0 0 ? S< 11月11 0:00 [kthrotld]
root 49 0.0 0.0 0 0 ? S< 11月11 0:00 [kmpath_rdacd]
root 50 0.0 0.0 0 0 ? S< 11月11 0:00 [kaluad]
root 51 0.0 0.0 0 0 ? S< 11月11 0:00 [kpsmoused]
root 53 0.0 0.0 0 0 ? S< 11月11 0:00 [ipv6_addrconf]
root 66 0.0 0.0 0 0 ? S< 11月11 0:00 [deferwq]
root 104 0.0 0.0 0 0 ? S 11月11 0:00 [kauditd]
root 283 0.0 0.0 0 0 ? S< 11月11 0:00 [mpt_poll_0]
1、计算每个用户的进程占了多少内存
awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}' top.txt
chrony, 1860KB
nginx, 3688KB
dbus, 2492KB
polkitd, 14152KB
root, 208636KB
2、统计当前目录下文件所占空间大小
ll -h | awk 'BEGIN {size=0} {size=size+$5} END{print size "KB"}'
3、统计 /etc/passwd 中用户数量
awk '{count++} END{print count}' /etc/passwd
4、统计有多少条 tcp 连接
(减掉 2 是因为前面两条字段不是 tcp 连接)
netstat -tnlp | awk '{count++} END{print count-2}'
5、打印出 11日到12日 13点到14点之间的日志信息
awk '{if ($2>10 && $2<12 && $3>"13:00:00" && $3<"14:00:00") print}' /var/log/messages