shell之awk技术
awk概述
- awk处理文本和数据的方式是这样,他逐行扫描文件,从第1行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作 (这里与sed一样),如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕)上;如果没有指定模式,则所有被操作所指定的行都被处理;gawk是awk的GUN版本。
语法格式
1 | options:-F:定义输入字段分隔符,默认的分隔符是空格或者制表符(tab) |
eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71[root@centos7 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
[root@centos7 shell]# awk 'BEGIN{FS=":";OFS="---"} {print $1,$2}' passwd
root---x
bin---x
daemon---x
halt---x
mail---x
operator---x
games---x
ftp---x
nobody---x
systemd-network---x
dbus---x
polkitd---x
[root@centos7 shell]# awk 'BEGIN{print 2/5} {print "ok"} END{print "----"}' passwd
0.4
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
----
[root@centos7 shell]# awk '{print $1}' passwd # 打印第一列,默认以空格和tab分割
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd
dbus:x:81:81:System
polkitd:x:999:998:User
[root@centos7 shell]# awk -F ":" '{print $1}' passwd # 打印第一列,设置以:分割,或写成awk 'BEGIN{FS=":"} {print $1}' passwd
root
bin
daemon
halt
mail
operator
games
ftp
nobody
systemd-network
dbus
polkitd
awk命令格式
1 | awk 'pattern' filename,pattern类似正则 |
awk原理
- 以
awk -F: '{print $1,$3}' passwd
为例:- ①awk使用一行作为输入,并捋这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束。
- ②然后,行被’:’(默认为空格或制表符)分解成字段(或域),每个字段存储在已编号的变量中,从$1开始,最多达100个字段。
- ③awk如何知道用空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空格。
- ④awk打印字段时,将以设置的方法使用print函数打印,awk在打印的字段间加上空格,因为$1,$3之间有一个逗号。逗号比较特殊,它映射为另一个内部变量,称为输出字段分隔符OFS,OFS默认为空格。
- ⑤awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕。
记录与字段相关的内部变量(man awk)
1 | [root@centos7 shell]# cat hosts |
awk的格式化输出
print函数。
1
2
3[root@centos7 shell]# date |awk '{print "Month: "$2"\nYear: "$NF}'
Month: 08月
Year: CSTprintf函数:精确控制输出格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18%s字符类型
%d数值类型
%f浮点类型
'-'表示左对齐。默认是右对齐
printf默认不会在行尾自动换行
[root@centos7 shell]# awk -F ":" '{printf "%-20s %-20s %-25s\n",$1,$2,$3}' passwd # %-20s:表示第1个字段占20个字符,-表示左对齐,s表示字符串
root x 0
bin x 1
daemon x 2
halt x 7
mail x 8
operator x 11
games x 12
ftp x 14
nobody x 99
systemd-network x 192
dbus x 81
polkitd x 999
awk模式和动作
任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发事件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。模式可以是任何条件语句或者复合语句或者正则表达式。模式包括2个特殊字段BEGIN和END,使用BEGIN语句设置计数和打印头。BEGIN语句使用在任何文本浏览动作之前,之后文本浏览动作依据输入文本开始执行。
END语句用来在awk完成文本浏览动作后,打印输出文本总数和结尾状态。模式可以是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59$0表示整行,~表示正则匹配,!表示不匹配
[root@centos7 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
[root@centos7 shell]# awk '/^root/' passwd # 等价于awk '$0 ~ /^root/' passwd
root:x:0:0:root:/root:/bin/bash
[root@centos7 shell]# awk '!/root/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
匹配字段:匹配操作符~,!~
[root@centos7 shell]# awk -F: '$1 ~ /^root/' passwd
root:x:0:0:root:/root:/bin/bash
[root@centos7 shell]# awk -F: '$1 !~ /^root/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
[root@centos7 shell]# awk -F: '$NF !~ /bash$/' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
awk比较表达式
1 | awk -F: '$3 == 0' /etc/passwd |
eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24awk '/west/' datafile # 匹配west
awk '/^north/' datafile # 匹配north开头
awk '$3 ~ /^north/' datafile # $3匹配不是以north开头的
awk '/^(no|so)/' datafile # 打印整行以no或者so开头的
awk '{print $3,$2}' datafile
awk '{print $3 $2}' datafile
awk '{print $0}' /etc/passwd
awk '{print "Number of fields:"NF}' /etc/passwd
awk '/northeast/{print $3,$2}' datafile # 满足northeast才打印
awk '/E/' datafile # 只要带有E就打印
awk '/^[ns]/{print $1}' datafie # 以n或者s开头才打印
awk '$5 ~ /\.[7-9]+/' datafile # 匹配.7到9的1到多个
awk '$2 !~ /E/{print $1,$2}' datafile # $2不匹配E,才打印
awk '$3 ~ /^Joel/{print $3 "is anice boy."}' datafile # $3匹配/^Joel/才打印
awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile # /[0-9][0-9]$/表示以数字结尾
awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile # 匹配Chin结尾的才print,$8代表第8列
awk '/Tj/{print $0}' datafile # 匹配Tj才打印
awk '$7 == 5' datafile
awk '$2 == "CT" {print $1,$2}' datafile
awk '$7 != 5' datafile
awk脚本编程
条件判断。
1
2
3
4
5
6
7
8{if(表达式){语句;语句;......}}
awk -F: '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd # 统计系统用户数
{if(表达式){语句} else{语句}}
awk -F: '{if($3==0){count++} else{i++}} END{print "管理员个数:"count;print "系统用户数:"i}}' /etc/passwd
{if(表达式){语句} else if(表达式){语句} else if(表达式){语句} else {语句}}
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i;print k;print j}}' /etc/passwd循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38[root@centos7 shell]# awk 'BEGIN{i=1;while(i<=10){print i;i++}}'
1
2
3
4
5
6
7
8
9
10
[root@centos7 shell]# awk -F: '/^root/{i=1;while(i<=7){print $i;i++}}' passwd
root
x
0
0
root
/root
/bin/bash
abc.txt文件内容:
111 222
333 444 555
666 777 888 999
[root@centos7 shell]# awk '{i=1;while(i<=NF){print $i;i++}} END{print "\n"}' abc.txt # print会换行
111
222
333
444
555
666
777
888
999
[root@centos7 shell]#
awk数组
awk不区分普通数组或者关联数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46[root@centos7 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
i从1开始
[root@centos7 shell]# awk -F: '{username[++i]=$1} END{print username[1]}' passwd
root
数组遍历:按照下标遍历
将需要统计的内容(某1字段)作为数组的索引,然后++即可
[root@centos7 shell]# awk -F: '{user[j++]=$1} END{for(i in user){print i,user[i]}}' passwd
4 mail
5 operator
6 games
7 ftp
8 nobody
9 systemd-network
10 dbus
11 polkitd
0 root
1 bin
2 daemon
3 halt
[root@centos7 shell]# awk -F: '{shells[$NF]++} END{for(i in shells){print i,shells[i]}}' passwd # 统计passwd中各种类型shell的数量
/bin/bash 1
/sbin/nologin 10
/sbin/halt 1
[root@centos7 shell]# netstat -ant |grep ':80' |awk '{status[$NF]++} END{for(i in status){print i,status[i]}}'
[root@centos7 shell]# ss -an |grep ':80' |awk '{status[$2]++} END{for(i in status){print i,status[i]}}'
[root@centos7 shell]# netstat -ant |grep ':80' |awk '{status[$NF]++} END{for(i in status){print i,status[i]}}' |sort -k2 -n |head
[root@centos7 shell]# ss -ant|grep ':80' |awk -F "[ :]*" '{ips[$(NF-2)]++} END{for(i in ips){print i,ips[i]}}' |sort -k2 -rn |head -5 # 统计当前访问的每个IP的数量
awk的变量,内置函数及外部变量
内置函数length:求出字符串长度。
1
2
3
4
5
6[root@centos7 shell]# awk -F: 'length($1)==4{count++;print $1} END{print "count is:"count}' passwd
root
halt
mail
dbus
count is:4内置函数:sub()查找替换(只找第一个),gsub类似于sed中的g(全局找)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24[root@centos7 shell]# var="bash"
方法一:在双引号的情况下使用
[root@centos7 shell]# echo "unix script" |awk "gsub(/unix/,\"$var\")"
bash script
方法二:在单引号的情况下使用
[root@centos7 shell]# echo "unix script" |awk 'gsub(/unix/,"'"$var"'")'
bash script
使用三引号
[root@centos7 shell]# i=10
[root@centos7 shell]# df -h |awk '{if((int($5)>'''$i''')){print $6":"$5}}'
/:33%
/boot:15%
[root@centos7 shell]# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/sda3 17G 5.1G 11G 33% /
devtmpfs 976M 0 976M 0% /dev
tmpfs 991M 0 991M 0% /dev/shm
tmpfs 991M 11M 981M 2% /run
tmpfs 991M 0 991M 0% /sys/fs/cgroup
/dev/sda1 976M 134M 776M 15% /boot
tmpfs 199M 12K 199M 1% /run/user/42
tmpfs 199M 0 199M 0% /run/user/0内置函数:int。
1
2
3[root@centos7 shell]# df -h |awk '{if((int($5)>5)){print $6":"$5}}'
/:33%
/boot:15%外部变量的使用,-v选项。
1
2[root@centos7 shell]# echo "unix script" |awk -v var="bash" 'gsub(/unix/,var)'
bash script