AWK是Linux下处理文本经常用到的神器,本文简单总结一些AWK的常见用法。
AWK是贝尔实验室1977年搞出来的文本出现神器,之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的Family Name的首字符。
- 内建变量
- 常见用法
- 一个项目中的实例
内建变量
awk常用的内建变量:
- $0: 当前记录(这个变量中存放着整个行的内容)
- $1~$n: 当前记录的第n个字段,字段间由FS分隔
- FS: 输入字段分隔符 默认是空格或Tab
- NF: 当前记录中的字段个数,就是有多少列
- NR: 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
- FNR: 当前记录数,与NR不同的是,这个值会是各个文件自己的行号
- RS: 输入的记录分隔符, 默认为换行符
- OFS: 输出字段分隔符, 默认也是空格
- ORS: 输出的记录分隔符,默认为换行符
- FILENAME: 当前输入文件的名字
常见用法
测试文本为test1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Proto 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 test:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 test:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 test:80 110.194.134.189:1032 ESTABLISHED
tcp 0 0 test:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 test:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 test:80 123.169.124.111:49829 ESTABLISHED
tcp 0 0 test:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 test:80 61.148.242.38:30901 ESTABLISHED
tcp 0 1 test:80 124.152.181.209:26825 FIN_WAIT1
tcp 0 0 test:80 110.194.134.189:4796 ESTABLISHED
tcp 0 0 test:80 183.60.212.163:51082 TIME_WAIT
tcp 0 1 test:80 208.115.113.92:50601 LAST_ACK
tcp 0 0 test:80 123.169.124.111:49840 ESTABLISHED
tcp 0 0 test:80 117.136.20.85:50025 FIN_WAIT2
tcp 0 0 :::22 :::* LISTEN
1. 输出文本第1列和第4例
比如文本是test
awk ‘{print $1, $4}’ test 或者 cat test | awk ‘{print $1, $4}’
1 | Proto Local-Address |
其中单引号中的被大括号括着的就是awk的语句,注意,其只能被单引号包含
2. 第三列的值为0 && 第6列的值为LISTEN
awk ‘$3==0 && $6==”LISTEN” ‘ test
或者
cat test | awk ‘$3==0 && $6==”LISTEN”‘
1 | cat test | awk '$3==0 && $6=="LISTEN"' |
其中的“==”为比较运算符。当然也可以使用其他比较运算符:!=, >, <, >=, <=
如果需要第一行的话,我们可以引入内建变量NR:
1 | cat test | awk '$3==0 && $6=="LISTEN" || NR==1' |
排除第一行
NR == 1表示第一行,所有只需要NR > 1即可排除第一行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
41docker images | awk '{print $3}'
IMAGE
3ab9bf2ca2ae
6dcff10458ed
0d9be02372b5
d88ab8e2621d
91edd2ee25bc
78b317f01851
03f0c8e66a3f
5d1c1efa947c
8563f56c190f
81c524fb1f3c
d311f26b1e1f
9cedadac57d9
8624e4df51ea
60950016331f
d57cff1bbe9e
7fb238f56747
5fde56634b93
8d13f4a4b67d
e1fc4e346a21
bb776ce48575
2aef0c7c6a81
566653aecc40
f2cd7c8a619e
31b9460465eb
5a21fbd7ad1c
7edb19ba7ce4
8a651548383b
04ff2f5d7aa0
67ad6ac4aa43
9f38484d220f
9361ce633ff1
fce289e99eb9
fce289e99eb9
fce289e99eb9
9e2c9d5f44ef
466c57ab0dc3
0bf029990080
138bd936fa29
2bef5870dbb0
排除第一行: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
41docker images | awk '{ if (NR > 1) print $3}'
3ab9bf2ca2ae
6dcff10458ed
0d9be02372b5
d88ab8e2621d
91edd2ee25bc
78b317f01851
03f0c8e66a3f
5d1c1efa947c
8563f56c190f
81c524fb1f3c
d311f26b1e1f
9cedadac57d9
8624e4df51ea
60950016331f
d57cff1bbe9e
7fb238f56747
5fde56634b93
8d13f4a4b67d
e1fc4e346a21
bb776ce48575
2aef0c7c6a81
566653aecc40
f2cd7c8a619e
31b9460465eb
5a21fbd7ad1c
7edb19ba7ce4
8a651548383b
04ff2f5d7aa0
67ad6ac4aa43
9f38484d220f
9361ce633ff1
fce289e99eb9
fce289e99eb9
fce289e99eb9
9e2c9d5f44ef
466c57ab0dc3
0bf029990080
138bd936fa29
2bef5870dbb0
关于行数的变化就可以使用NR来灵活处理
3. 指定分隔符
1 | awk 'BEGIN{FS=":"} {print $1,$3,$6}' /etc/passwd |
上面的命令也等价于:(-F的意思就是指定分隔符):awk -F: ‘{print $1,$3,$6}’ /etc/passwd
如果你要指定多个分隔符,可以这样使用:awk -F ‘[;:]’
4. 字符串匹配
1 | cat 1 | awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' |
其中NR用于打印出行号,该示例用于匹配FIN字样状态。
~表示模式开始,//中是模式,这就是一个正则表达式的匹配。
其他一些常见用法可以参考耗子叔的AWK 简明教程
一个项目中的实例
在实际的项目中,经常会用到shell脚本,里面经常会对一些打印做处理,也就有了awk的用武之地: )
比如脚本中需要获取打印中的某几列的值,然后根据另一关键字来获取当前想要的值。
当然获取某几列的值,其实很简单就是上面介绍的第一种用法。但是由于项目组其他成员经常会改打印,提出要求希望能够在脚本中去获取含有某关键字的值,而不是写死获取某一列。
打印例子存于true.log:
1 | AZ ID| RACK ID| POOL ID| SERVER ID| STORAGE_IP | |
比如就如上的打印,希望能根据IP获取当前的RACK ID 和 SERVER ID,但不能写死打印1、3、5列。
运行:1
cat true.log | awk 'BEGIN{FS="|"}{for(i=1;i<=NF;i++) {if($i ~ /RACK ID/) n=i;if($i ~ /SERVER ID/) m=i;if($i ~ /STORAGE_IP/) k=i}}{print $n,$m,$k}'
1 | RACK ID SERVER ID STORAGE_IP |
这样再稍加处理就可以完成任务了: )
1 | cat true.log | awk 'BEGIN{FS="|"}{for(i=1;i<=NF;i++) {if($i ~ /RACK ID/) n=i;if($i ~ /SERVER ID/) m=i;if($i ~ /STORAGE_IP/) k=i}}{print $n,$m,$k}' | grep -w "192.168.0.2" | head -1 |
这样就能很OK获取到某一节点STORAGE_IP的RACK ID和SERVER ID了。