文本处理三剑客之 awk-9

偷得浮生 生而为人 本文由博客端 HTTPS://47.110.149.21 主动推送
本贴最后更新于 333 天前,其中的信息可能已经斗转星移

4 文本处理三剑客之 awk

awk:Aho,Weinberger,Kernighan,报告生成器,格式化文本输出,GNU/Linux 发布的 AWK 目前由自由软件基金会(FSF)进行开发维护,通常也称为 GNU AWK

版本:

gawk:模式扫描和处理语言,可以实现下面功能

格式:

awk [options] 'program' var=value file...
awk [options] -f programfile var=value file...

说明:
program 通常是被放在单引号中,并可以由三种部分组成

常见选项:

Program 格式:

pattern{action statements;...}

pattern:决定动作语句何时触发即触发事件,比如:BEGIN,END,正则表达式等

action statements:对数据进行处理,放在{}内指明,常见:print,printf

awk 工作过程:

clipboard.png
第一步:执行 BEGIN{action;...}语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行 pattern{ action;...}语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时执行 END{action;...}语句块
BEGIN 语句块在 awk 开始从输入流中读取行之前被执行,这是一个可选择的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在 BEGIN 语句块中
END 语句块在 awk 从输入流中读取完所有的行之后立即被执行,比如打印所有行的分析结果这类信息总汇都是在 END 语句块中完成,它也是一个可选语句块
pattern 语句块中的通用命令是最重要的部分,也是可选的。如果没有提供 pattern 语句块,则默认执行{print},即打印每一个读取到的行,awk 读取的每一行都会执行该语句块

分割符、域和记录

常用的 action 分类

awk 控制语句

4.1 动作 print

格式:

print item1,item2,...

说明:

范例:

[18:01:30 root@centos8 boot]#awk '{print "hello,awk"}'
[19:02:55 root@centos8 boot]#seq 10 | awk '{print "hello,awk"}'
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
hello,awk
[19:03:50 root@centos8 boot]#seq 3 | awk '{print 2*3}'
6
6
6
[19:04:06 root@centos8 boot]#awk -F: '{print "zhang"}' /etc/passwd
[19:04:42 root@centos8 boot]#awk -F: '{print }' /etc/passwd
[19:05:00 root@centos8 boot]#awk -F: '{print $0}' /etc/passwd
[19:05:53 root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd
[19:06:55 root@centos8 ~]#awk -F: '{print $1"\t"$3}' /etc/passwd

[19:07:12 root@centos8 ~]#grep "^UUID" /etc/fstab | awk '{print $2,$3}'
/boot ext4

面试题:取出网站访问量最大的前 3 个 IP

[19:11:46 root@centos8 ~]#awk '{print $1}' nginx.access.log-20200428 |sort |uniq -c | sort -nr |head -3
5498 122.51.38.20
2161 117.157.173.214
953 211.159.177.120

[19:11:55 root@centos8 ~]#awk '{print $1}' nginx.access.log-20200428 |sort |uniq -c|sort -nr |head
5498 122.51.38.20
2161 117.157.173.214
953 211.159.177.120
219 58.87.87.99
100 222.218.17.189
100 218.201.62.71
100 122.139.5.237
100 120.195.144.116
100 118.121.41.14
100 1.177.191.161

面试题:取出分区利用率

[19:16:38 root@centos8 ~]#df | awk '{print $1,$5}'
Filesystem Use%
devtmpfs 0%
tmpfs 0%
tmpfs 3%
tmpfs 0%
/dev/mapper/cl-root 14%
/dev/sda1 14%
tmpfs 0%

#使用扩展正则表达式
[19:17:58 root@centos8 ~]#df | awk -F" +|%" '{print $1,$5}'
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 3
tmpfs 0
/dev/mapper/cl-root 14
/dev/sda1 14
tmpfs 0
[19:19:51 root@centos8 ~]#df | grep "^/dev/" | awk -F" +|%" '{print $1,$5}'
/dev/mapper/cl-root 14
/dev/sda1 14
[19:20:35 root@centos8 ~]#df | awk -F" +|%" '/^\/dev\//{print $1,$5}'
/dev/mapper/cl-root 14
/dev/sda1 14

面试题:取 nginx 访问日志中 IP 和时间

[19:23:33 root@centos8 ~]#head -n 3 nginx.access.log-20200428
58.87.87.99 - - [27/Apr/2020:03:10:51 +0800] "POST /wp-cron.php?doing_wp_cron=1587928251.0032949447631835937500 HTTP/1.1" ""sendfileon
61.131.3.225 - - [27/Apr/2020:03:10:51 +0800] "GET / HTTP/1.1" ""sendfileon
157.245.106.153 - - [27/Apr/2020:03:10:52 +0800] "GET /wp-login.php HTTP/1.1" ""sendfileon
[19:23:56 root@centos8 ~]#awk -F"[[ ]" '{print $1,$5}' nginx.access.log-20200428 | head -3
58.87.87.99 27/Apr/2020:03:10:51
61.131.3.225 27/Apr/2020:03:10:51
157.245.106.153 27/Apr/2020:03:10:52

面试题:取 ifconfig 输出结果中的 IP 地址

[19:26:21 root@centos8 ~]#ifconfig eth0| awk '/netmask/{print $2}'
192.168.10.81
#这个使用变量的写法
[19:27:03 root@centos8 ~]#ifconfig eth0| awk 'NR==2{print $2}'
192.168.10.81

面试题:文件 host_list.log 如下格式,请提取“.magedu.com”前面的主机名部分并写入到该文件中

[19:30:36 root@centos8 ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
[19:31:25 root@centos8 ~]#awk -F"[. ]" '{print $2}' host_list.log
www
blog
study
linux
python
[19:31:28 root@centos8 ~]#awk -F"[. ]" '{print $2}' host_list.log >>host_list.log
[19:31:38 root@centos8 ~]#cat host_list.log
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
www
blog
study
linux
python

4.2 awk 变量

awk 中的变量分为:内置和自定义变量

4.2.1 常见的内置变量

范例:

#下面这四种写法输出的结果都差不多
[19:31:42 root@centos8 ~]#awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
[19:34:37 root@centos8 ~]#awk -v FS=':' '{print $1FS$3}' /etc/passwd
[19:35:24 root@centos8 ~]#awk -F: '{print $1,$3,$7}' /etc/passwd
[19:36:22 root@centos8 ~]#S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd

#-F 和 FS变量功能一样,同时使用会冲突,排在后面的会生效
[09:01:27 root@centos8 ~]#awk -v FS=':' -F";" '{print $1FS$3}' /etc/passwd | head -n3
root:x:0:0:root:/root:/bin/bash;
bin:x:1:1:bin:/bin:/sbin/nologin;
daemon:x:2:2:daemon:/sbin:/sbin/nologin;
[09:01:47 root@centos8 ~]#awk -v FS=';' -F":" '{print $1FS$3}' /etc/passwd | head -n3
root:0
bin:1
daemon:2

范例:

19:44:28 root@centos8 ~]#awk -v FS=":" '{print $1,$3,$7}' /etc/passwd | head -1
root 0 /bin/bash
[19:46:27 root@centos8 ~]#awk -v FS=":" -v OFS=':' '{print $1,$3,$7}' /etc/passwd | head -1
root:0:/bin/bash

范例:

[19:50:47 root@centos8 ~]#awk -v RS='' '{print $1,$2,$3}' /etc/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

范例:

[19:53:41 root@centos8 ~]#awk -v ORS='+++' -F':' '{print $1,$3,$7}' /etc/passwd | head -3

范例:

#引用变量时,变量前不需加$
[19:55:02 root@centos8 ~]#awk -F: '{print NF}' /etc/passwd 
7
[19:55:46 root@centos8 ~]#awk -F: '{print $(NF-1)}' /etc/passwd
/root
/bin

[19:58:58 root@centos8 Packages]#ls /mnt/AppStream/Packages/ | awk -F. '{print $(NF-1)}'|sort|uniq -c
895 i686
1953 noarch
1 TRANS
2478 x86_64

面试题:主机连接数最多的前 3 个 IP

[20:02:46 root@zhangzhuo ~]#ss -nta | awk -F" +|:" '{print $(NF-2)}' | sort |uniq -c |sort -nr | head -n 3
5 172.18.0.2
5 127.0.0.1
5 127.0.0.1
[20:05:09 root@zhangzhuo ~]#ss -nta | awk -F" +|:" '/^ESTAB/{print $(NF-2)}' | sort |uniq -c | sort -rn|head -n3
5 172.18.0.2
5 127.0.0.1]
5 127.0.0.1

范例:每十分钟检查将连接数超过 100 个以上的 IP 放入黑名单拒绝访问

[10:04:39 root@centos8 ~]#cat deny_dos.sh
LINK=100
ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}' | sort | uniq -c | while read count ip;do
if [ $count -gt $LINK ];then
iptables -A INPUT -s $ip -j REJECT
fi
done
[10:06:00 root@centos8 ~]#chmod +x /root/deny_dos.sh
[10:06:42 root@centos8 ~]#crontab -l
*/10 * * * * /root/deny_dos.sh

范例:

[20:06:59 root@centos8 Packages]#awk '{print NR,$0}' /etc/issue /etc/centos-release
1 \S
2 Kernel \r on an \m
3
4 CentOS Linux release 8.2.2004 (Core)

范例:取 ifconfig 输出结果中的 IP 地址

[10:12:16 root@centos8 ~]#ifconfig ens33 | awk '/netmask/{print $2}'
192.168.10.81
[10:12:40 root@centos8 ~]#ifconfig ens33 | awk 'NR==2{print $2}'
192.168.10.81

范例:

[10:13:15 root@centos8 ~]#awk -F: '{print NR}' /etc/passwd
1
2
3
.....
#文本内容结束后执行
[10:13:35 root@centos8 ~]#awk -F: 'END{print NR}' /etc/passwd
24
#文本内容开始前执行
[10:14:06 root@centos8 ~]#awk -F: 'BEGIN{print NR}' /etc/passwd
0

范例:

[10:22:10 root@centos8 ~]#awk '{print NR,$0}' /etc/issue /etc/redhat-release
1 \S
2 Kernel \r on an \m
3
4 CentOS Linux release 8.2.2004 (Core)
[10:22:44 root@centos8 ~]#awk '{print FNR,$0}' /etc/issue /etc/redhat-release
1 \S
2 Kernel \r on an \m
3
1 CentOS Linux release 8.2.2004 (Core)

范例:

[10:26:01 root@centos8 ~]#awk '{print FNR,FILENAME,$0}' /etc/issue
1 /etc/issue \S
2 /etc/issue Kernel \r on an \m
3 /etc/issue
[10:26:08 root@centos8 ~]#awk '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release
1 /etc/issue \S
2 /etc/issue Kernel \r on an \m
3 /etc/issue
1 /etc/redhat-release CentOS Linux release 8.2.2004 (Core)

范例:

[10:26:24 root@centos8 ~]#awk '{print ARGC}' /etc/issue /etc/redhat-release
3
3
3
3
[10:27:34 root@centos8 ~]#awk 'BEGIN{print ARGC}' /etc/issue /etc/redhat-release
3

范例:

[20:14:45 root@centos8 Packages]#awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/centos-release
awk
[20:16:53 root@centos8 Packages]#awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/centos-release
/etc/issue
[20:16:58 root@centos8 Packages]#awk 'BEGIN{print ARGV[2]}' /etc/issue /etc/centos-release
/etc/centos-release
[20:17:02 root@centos8 Packages]#awk 'BEGIN{print ARGV[3]}' /etc/issue /etc/centos-release

4.2.2 自定义变量

自定义变量是区分字符大小写的,使用下面方式进行赋值

范例:

[20:17:06 root@centos8 Packages]#awk -v test1=test2="hello,gawk" 'BEGIN{print test1,test2}'
test2=hello,gawk

[20:19:28 root@centos8 Packages]#awk -v test1=test2="hello1,gawk" 'BEGIN{test1=test2="hello2,gawk";print test1,test2}'
hello2,gawk hello2,gawk
#注意俩种方式的区别

范例:

[20:20:50 root@centos8 Packages]#awk -v test='hello gawk' '{print test}'  /etc/fstab
[20:22:37 root@centos8 Packages]#awk -v test='hello gawk' 'BEGIN{print test}'
[20:23:43 root@centos8 Packages]#awk 'BEGIN{test="hello,gawk";print test}'
[20:23:50 root@centos8 Packages]#awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd
root male
bin male 18
daemon male 18

#脚本赋值引用
[20:27:58 root@centos8 ~]#cat awkscript
{print script,$1,$2}
[20:28:07 root@centos8 ~]#awk -F: -f awkscript script="awk" /etc/passwd
awk root x
awk bin x

4.3 动作 printf

printf 可以实现格式化输出

格式:

printf "FORMAT",item1,item2,...

说明:

格式符:与 item 一 一对应

%c:显示字符的ASCII码
%d,%i:显示十进制整数
%e,%E:显示科学计数法数值
%f:显示为浮点数
%g,%G:以科学计数法或浮点形式显示数值
%s:显示字符串
%%:显示%自身

修饰符

#[.#] 第一个数字控制显示的宽度;第二个#表示小数点后精度,如:%3.1f
- 左对齐(默认右对齐) 如:%-15%
+ 显示数值的正符号     如:%+d

范例:

[20:29:25 root@centos8 ~]#awk -F: '{printf "%s",$1}' /etc/passwd
[20:30:49 root@centos8 ~]#awk -F: '{printf "%s\n",$1}' /etc/passwd
root
bin
[20:30:54 root@centos8 ~]#awk -F: '{printf "+s\n",$1}' /etc/passwd
root
bin
[20:31:11 root@centos8 ~]#awk -F: '{printf "%-20s\n",$1}' /etc/passwd
root
bin
[20:31:49 root@centos8 ~]#awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
root                          0
bin                           1
[20:32:22 root@centos8 ~]#awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
Username: root
Username: bin
[20:34:05 root@centos8 ~]#awk -F: '{printf "Username: %sUID:%d\n",$1,$3}' /etc/passwd
Username: rootUID:0
Username: binUID:1
[20:34:16 root@centos8 ~]#awk -F: '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd
Username:                      rootUID:0
Username:                       binUID:1
[20:34:54 root@centos8 ~]#awk -F: '{printf "Username: %-25sUID:%d\n",$1,$3}' /etc/passwd
Username: root                     UID:0
Username: bin                      UID:1

4.4 操作符

4.4.1 算数操作符:

x+y,x-y,x*y,x/y,x^2,x%y
-x:转换为负数
+x将字符串转换为数值

字符串操作符:没有符号的操作符,字符串连接

赋值操作符:

=, +=, -=, *=, /=, %=, ^=,++, --

范例:

[20:35:23 root@centos8 ~]#awk 'BEGIN{i=0;print i++,i}'
0 1
[20:48:34 root@centos8 ~]#awk 'BEGIN{i=0;print ++i,i}'
1 1

范例:'0{print "1"}',' ""{print "1"}'表示假后面的不打印,其余为真打印,'n++'等于'n{print $0}'表示打印所有行

[20:48:41 root@centos8 ~]#seq 10 | awk 'n++'
2
3
4
5
6
7
8
9
10
[20:49:49 root@centos8 ~]#awk -v n=0 '!n++' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[20:49:54 root@centos8 ~]#awk -v n=0 '!n++{print n}' /etc/passwd
1
[20:50:26 root@centos8 ~]#awk -v n=1 '!n++{print n}' /etc/passwd
[20:51:02 root@centos8 ~]#awk -v n=0 '!++n{print n}' /etc/passwd

[20:51:06 root@centos8 ~]#awk -v n=0 '!++n' /etc/passwd
[20:52:04 root@centos8 ~]#awk -v n=-1 '!++n' /etc/passwd
root:x:0:0:root:/root:/bin/bash

4.4.2 比较操作符:

==, !=, >, >=, <, <=

范例:

[20:55:40 root@centos8 ~]#awk 'NR==2' /etc/issue
Kernel \r on an \m
[20:57:28 root@centos8 ~]#awk -F: '$3>=1000' /etc/passwd
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
zhang:x:1000:1000::/home/zhang:/bin/bash

范例:取奇,偶数行

[20:58:32 root@centos8 ~]#seq 10 | awk 'NR%2==0'
2
4
6
8
10
[20:58:38 root@centos8 ~]#seq 10 | awk 'NR%2==1'
1
3
5
7
9
[20:58:53 root@centos8 ~]#seq 10 | awk 'NR%2!=0'
1
3
5
7
9

4.4.3 模式匹配符:

~   左边是否和右边匹配,包含关系
!~  是否不匹配

范例:

[09:14:01 root@centos8 ~]#awk -F: '$0 ~ /root/{print $1}' /etc/passwd
root
operator
[09:14:34 root@centos8 ~]#awk -F: '$0 ~ "^root"{print $1}' /etc/passwd
root
[09:15:24 root@centos8 ~]#awk '$0 !~ /root/' /etc/passwd
[09:15:27 root@centos8 ~]#awk '/root/' /etc/passwd
[09:16:10 root@centos8 ~]#awk -F: '/r/' /etc/passwd
[09:16:47 root@centos8 ~]#awk -F: '$3==0' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[09:16:51 root@centos8 ~]#df | awk -F" +|%" '$0 ~ /\/dev\//{print $5}'
0
14
14
[09:18:15 root@centos8 ~]#ifconfig eth0 | awk 'NR==2{print $2}'
192.168.10.81

4.4.4 逻辑操作符:

与:&& 并且关系
或:|| 或者关系
非: ! 取反

范例:!取反

[09:18:50 root@centos8 ~]#awk 'BEGIN{print i}'

[09:21:30 root@centos8 ~]#awk 'BEGIN{print !i}'
1
[09:21:39 root@centos8 ~]#awk -v i=10 'BEGIN{print !i}'
0
[09:21:52 root@centos8 ~]#awk -v i=-3 'BEGIN{print !i}'
0
[09:22:00 root@centos8 ~]#awk -v i=0 'BEGIN{print !i}'
1
[09:22:11 root@centos8 ~]#awk -v i=abc 'BEGIN{print !i}'
0
[09:22:15 root@centos8 ~]#awk -v i='' 'BEGIN{print !i}'
1

范例:

[09:23:25 root@centos8 ~]#awk -F: '$3>=0 && $3<=1000{print $1,$3}' /etc/passwd
root 0
bin 1
[09:23:29 root@centos8 ~]#awk -F: '$3==0 || $3>=1000{print $1,$3}' /etc/passwd
root 0
nobody 65534
zhang 1000
[09:25:11 root@centos8 ~]#awk -F: '!($3==0){print $1,$3}' /etc/passwd
bin 1
daemon 2
[09:26:24 root@centos8 ~]#awk -F: '!($3>=500){print $1,$3}' /etc/passwd
root 0
bin 1

4.4.5 条件表达式(三目表达式)

selector?if-true-expression:if-false-expression

范例:

[09:26:28 root@centos8 ~]#awk -F: '{$3>=1000?usertype="Common User":usertype="Sysuser";printf "%-20s:%12s\n",$1,usertype}' /etc/passwd
root                :     Sysuser
[09:35:19 root@centos8 ~]#df | awk -F"[ %]+" '/\/dev\//{$(NF-1)>10?disk="full":disk="OK";print $(NF-1),disk}'
0 OK
14 full
14 full

4.5 模式 PATTERN

PATTERN:根据 pattern 条件,过滤匹配的行,在做处理

范例:

[09:35:43 root@centos8 ~]#awk -F: '{print $1,$3}' /etc/passwd

范例

[09:39:26 root@centos8 ~]#awk '/^UUID/{print $1}' /etc/fstab
UUID=df5fee56-9dd8-4814-bed7-6449e315bae7
[09:41:11 root@centos8 ~]#awk '!/^UUID/{print $1}' /etc/fstab
[09:41:23 root@centos8 ~]#df | awk '/^\/dev\//'
/dev/mapper/cl-root   17G  2.3G   15G  14% /
/dev/sda1            976M  124M  786M  14% /boot

范例:

[09:41:46 root@centos8 ~]#seq 10 | awk '1'
1
2
3
4
5
6
7
8
9
10
[09:44:26 root@centos8 ~]#seq 10 | awk '0'
[09:44:31 root@centos8 ~]#seq 10 | awk '"false"'
1
2
3
4
5
6
7
8
9
10
[09:44:41 root@centos8 ~]#seq 10 | awk '""'
[09:44:48 root@centos8 ~]#seq 10 | awk '"0"'
1
2
3
4
5
6
7
8
9
10
[09:44:53 root@centos8 ~]#seq 10 | awk 'true'    这里是当成变量了因为没有赋值所以为空假
[09:45:06 root@centos8 ~]#seq 10 | awk 'false'
[09:45:28 root@centos8 ~]#seq 10 | awk '0'
[09:45:35 root@centos8 ~]#seq 10 | awk '""'
[09:45:37 root@centos8 ~]#seq 10 | awk '" "'
1
2
3
4
5
6
7
8
9
10
[09:47:51 root@centos8 ~]#seq 10 | awk -v magedu=0 'magedu'
[09:47:58 root@centos8 ~]#seq 10 | awk -v magedu="0" 'magedu'
[09:48:11 root@centos8 ~]#seq 10 | awk -v magedu="" 'magedu'
[09:48:16 root@centos8 ~]#seq 10 | awk -v magedu="a" 'magedu'
1
2
3
4
5
6
7
8
9
10

范例:

[09:49:25 root@centos8 ~]#awk '1' /etc/passwd
[09:49:23 root@centos8 ~]#awk '!1' /etc/passwd
[09:49:27 root@centos8 ~]#awk '!0' /etc/passwd

范例

[09:50:46 root@centos8 ~]#seq 10 | awk 'i=0'
[09:51:48 root@centos8 ~]#seq 10 | awk 'i=1'
1
2
3
4
5
6
7
8
9
10
[09:51:50 root@centos8 ~]#seq 10 | awk 'i=!i'
1
3
5
7
9
[09:52:00 root@centos8 ~]#seq 10 | awk '{i=!i;print i}'
1
0
1
0
1
0
1
0
1
0
[09:52:15 root@centos8 ~]#seq 10 | awk '!(i=!i)'
2
4
6
8
10
[09:52:34 root@centos8 ~]#seq 10 | awk -v i=1 'i=!i'
2
4
6
8
10

范例:

[09:57:14 root@centos8 ~]#awk -F: 'i=1;j=1{print i,j}' /etc/passwd
[09:57:40 root@centos8 ~]#awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
zhang /bin/bash
[09:58:47 root@centos8 ~]#awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
root /bin/bash
zhang /bin/bash

范例:

[09:59:24 root@centos8 ~]#seq 10 | awk 'NR>=3 && NR<=6'
3
4
5
6
[10:01:46 root@centos8 ~]#awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
[10:02:27 root@centos8 ~]#sed -n '3,6p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
[10:02:50 root@centos8 ~]#awk '/^bin/,/^adm/' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
[10:03:12 root@centos8 ~]#sed -n '/^bin/,/^adm/p' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin

范例:

[10:03:51 root@centos8 ~]#awk -F: 'BEGIN{print "USER USERID"} {print $1":"$3} END{print "END FILE"}' /etc/passwd
USER USERID
root:0
[10:08:31 root@centos8 ~]#awk -F: '{print "USER USERID";print $1":"$3} END{print "END FILE"}' /etc/passwd

[10:18:14 root@centos8 ~]#awk -F: 'BEGIN{printf "---------------------------------\n|%-20s|%10s|\n---------------------------------\n","username","uid"}{printf "|%-20s|%10d|\n",$1,$3}END{printf "---------------------------------\n"}' /etc/passwd

4.6 条件判断 if-else

语法:

#双分支判断
if(condition){statement;…}[else statement]
#多分支判断
if(condition1){statement1}else if(condition2){statement2}else if(condition3){statement3}...... else {statementN}

使用场景:对 awk 取得的整数行或某个字段做条件判断

范例:

[10:18:18 root@centos8 ~]#awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
[10:23:17 root@centos8 ~]#awk -F: '{if($NF=="/bin/bash")print $1,$3}' /etc/passwd
[10:23:35 root@centos8 ~]#awk '{if(NF>5)print $0}' /etc/fstab
#下面是俩种格式一样的功能
[10:30:38 root@centos8 ~]#awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else{printf "root or Sysuser: %s\n",$1}}' /etc/passwd
[10:30:38 root@centos8 ~]#awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or Sysuser: %s\n",$1}' /etc/passwd

[10:35:49 root@centos8 ~]#df -h | awk -F% '/^\/dev\//{print $1}' | awk '$NF>=10{print $1,$5}'
[10:37:01 root@centos8 ~]#df | awk -F"[ %]+" '/^\/dev\//{if($5>10)print $1,$5}

[10:39:42 root@centos8 ~]#awk 'BEGIN{test=58;if(test>90){print "very good"}else if(test>60){print "good"}else{print "no pass"}}'

4.7 条件判断 switch

语法:

功能:类似于 shell 中的 case 判断语句

switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}

4.8 循环 while

语法:

while (condition) {statement;…}

条件“真”,进入循环;条件“假”,退出循环

使用场景:

范例:

[10:56:45 root@centos8 ~]#awk -v i=1 -v sum=0 'BEGIN{while(i<=100){sum+=i;i++};print sum}'
5050
#使用awk要比shell中循环求和速度要快
[11:03:59 root@centos8 ~]#time (awk -v i=1 -v sum=0 'BEGIN{while(i<=1000000){sum+=i;i++};print sum}')
500000500000

real	0m0.062s
user	0m0.059s
sys	0m0.002s
[11:04:32 root@centos8 ~]#time (./1000000sum.sh)
500000500000

real	0m4.441s
user	0m4.406s
sys	0m0.001s

示例:

#内置函数length()返回字符数,而非字节数
[11:06:54 root@centos8 ~]#awk 'BEGIN{print length("hello")}'
5
[11:07:07 root@centos8 ~]#awk 'BEGIN{print length("张卓")}'
2
[11:14:41 root@centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=/dev/mapper/centos-root 28
ro 2
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
rhgb 4
quiet 5
net.ifnames=0 13
biosdevname=0 13
linux16 7
/vmlinuz-0-rescue-89e3a70685b441a0a1e8546f09fcc342 50
root=/dev/mapper/centos-root 28
ro 2
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
rhgb 4
quiet 5
net.ifnames=0 13
biosdevname=0 13

[11:18:02 root@centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=10){print $i,length($i)};i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=/dev/mapper/centos-root 28
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
net.ifnames=0 13
biosdevname=0 13
/vmlinuz-0-rescue-89e3a70685b441a0a1e8546f09fcc342 50
root=/dev/mapper/centos-root 28
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
net.ifnames=0 13
biosdevname=0 13

4.9 循环 do-while

语法:

do {statement;…}while(condition)

意义:无论真假,至少执行一次循环体

范例:

[11:22:34 root@centos8 ~]#awk 'BEGIN{ total=0;i=1;do{total+=i;i++;}while(i<=100);print total}'
5050

4.10 循环 for

语法:

for(expr1;expr2;expr3) {statement;…}

常见用法:

for(variable assignment;condition;iteration process) {for-body}

特殊用法:能够遍历数组中的元素

for(var in array) {for-body}

范例:

[11:22:49 root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum+=i};print sum}'
5050
[11:25:14 root@centos8 ~]#for((i=1,sum=0;i<=100;i++));do let sum+=i;done ;echo $sum
5050

范例:

[11:30:21 root@centos7 ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++){if(length($i)>10){print $i,length($i)}}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1127.el7.x86_64 31
root=/dev/mapper/centos-root 28
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
net.ifnames=0 13
biosdevname=0 13
/vmlinuz-0-rescue-89e3a70685b441a0a1e8546f09fcc342 50
root=/dev/mapper/centos-root 28
spectre_v2=retpoline 20
rd.lvm.lv=centos/root 21
rd.lvm.lv=centos/swap 21
net.ifnames=0 13
biosdevname=0 13

4.11 continue 和 break

格式:

continue [n]
break [n]

范例:

[11:26:14 root@centos8 ~]#awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)continue;sum+=i};print sum}'
5000
[11:33:27 root@centos8 ~]#awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)break;sum+=i};print sum}'
1225
[11:33:41 root@centos8 ~]#awk 'BEGIN{for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i};print sum}'
2500

4.12 next

next 可以提前结束对本行处理而直接进入下一行处理(awk 自身循环)

范例:

[11:37:09 root@centos8 ~]#awk -F: '{if($3%2!=0)next;print$1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
ftp 14
nobody 65534
polkitd 998
sssd 996
sshd 74
saslauth 994
rpc 32
tcpdump 72
zhang 1000
pcp 992
apache 48
cockpit-wsinstance 990
说明:本来条件是处理uid是奇数的行,假next之后不处理这行跳过直接打印下一行

4.13 数组

awk 的数组为关联数组

格式:

array_name[index-expression]

范例:

weekdays["mon"]="Monday"

index-expression

范例:

[11:37:16 root@centos8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday

范例:很难理解多看下

[11:44:17 root@centos8 ~]#awk '!line[$0]++' dupfile
1
2
3
4
5
[11:44:21 root@centos8 ~]#awk '{print !line[$0]++,$0,line[$0]}' dupfile
1 1 1
1 2 1
1 3 1
1 4 1
1 5 1
[11:45:52 root@centos8 ~]#awk '{!line[$0]++;print $0,line[$0]}' dupfile
1 1
2 1
3 1
4 1
5 1
[11:47:19 root@centos8 ~]#cat dupfile
1
2
3
4
5

范例:判断数组索引是否存在

[11:48:06 root@centos8 ~]#awk 'BEGIN{array["i"]="x";array["j"]="y";print "i" in array,"y" in array}'
1 0
[11:49:57 root@centos8 ~]#awk 'BEGIN{array["i"]="x";array["j"]="y";if("i" in array){print "存在"}else{print "不存在"}}'
存在
[11:51:58 root@centos8 ~]#awk 'BEGIN{array["i"]="x";array["j"]="y";if("a" in array){print "存在"}else{print "不存在"}}'
不存在

4.13.1 若要遍历数组中每个元素,要使用 for 循环

for(var in array) {for-body}

注意:var 会遍历 array 的每个索引

范例:遍历数组

[11:54:23 root@centos8 ~]#awk 'BEGIN{zhang["mon"]="Monday";zhang["tue"]="Tuesday";for(i in zhang){print i,zhang[i]}}'
tue Tuesday
mon Monday

[11:56:13 root@centos8 ~]#awk 'BEGIN{students[1]="daizong";students[2]="junzong";students[3]="xiaohong";for(x in students){print x,students[x]}}'
1 daizong
2 junzong
3 xiaohong

[12:01:43 root@centos8 ~]#awk 'BEGIN{ a["x"] = "welcome"
a["y"] = "to"
a["z"] = "zhangzhuo"
for (i in a){
print i,a[i]
}
}'
x welcome
y to
z zhangzhuo

[12:01:48 root@centos8 ~]#awk -F: '{user[$1]=$3}END{for(i in user){print "username: "i,"uid: "user[i]}}' /etc/passwd
username: tcpdump uid: 72
username: rpc uid: 32
username: sshd uid: 74

范例:显示主机的连接状态出现的次数

[14:08:30 root@centos8 ~]#ss -nta |awk 'NR!=1{print $1}' |sort |uniq -c
1 ESTAB
7 LISTEN
[14:10:20 root@centos8 ~]#ss -nta |awk 'NR!=1{state[$1]++}END{for(i in state){print i,state[i]}}'
LISTEN 7
ESTAB 1

范例:查看 nginx 服务访问次数

[14:15:05 root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access.log
[14:15:05 root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access.log | sort -nr | head  -3
1586 114.246.83.58
1424 222.131.157.24
1092 123.123.104.28
[14:18:31 root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access.log | sort -k2rn | head  -3
114.246.83.58 1586
222.131.157.24 1424
123.123.104.28 1092

范例:封掉查看访问日志中连接次数超过 1000 次的 IP

[14:23:58 root@centos8 ~]#awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' access.log

范例:多维数组,遍历的话也得遍历 2 次

[14:24:36 root@centos8 ~]#awk 'BEGIN{

> array[1][1]=11
> array[1][2]=12
> array[1][3]=13
> for (i in array)
> for (j in array[i])
> print array[i][j]
> }'
> 11
> 12
> 13

范例:

[14:35:49 root@centos8 ~]#cat score.txt
name sex score
alice f 100
bob   m 90
ming  m 95
hong  f 90
[14:35:53 root@centos8 ~]#awk 'NR!=1{if($2=="m"){m_sum+=$3;m_num++}else{f_sum+=$3;f_num++}}END{print "男 生平均成绩="m_sum/m_num,"女生平均成绩="f_sum/f_num}' score.txt
男生平均成绩=92.5 女生平均成绩=95

[14:35:54 root@centos8 ~]#awk 'NR!=1{score[$2]+=$3;num[$2]++}END{for(i in score){print i,score[i]/num[i]}}' score.txt
m 92.5
f 95

[14:40:15 root@centos8 ~]#awk 'NR!=1{num[$2]+=$3;sum[$2]++}END{for(i in num){if(i=="m"){print "男生平均成绩="num[i]/sum[i]}else{print "女生平均成绩="num[i]/sum[i]}}}' score.txt
男生平均成绩=92.5
女生平均成绩=95

4.14 awk 函数

awk 的函数分为内置和自定义函数

官方文档

https://www.gnu.org/software/gawk/manual/gawk.html#Functions

4.14.1 常见内置函数

rand():返回0和1之间一个随机数
srand():配合rand() 函数,生成随机数的种子
int():返回整数

范例:

[14:42:24 root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.534234
[14:45:31 root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.642982
[14:45:33 root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
0.741868
[14:46:41 root@centos8 ~]#awk 'BEGIN{srand();for (i=1;i<=10;i++)print int(rand()*100)}'
47
85
85
32
99
29
83
84
85
45
length([s]):返回指定字符串的长度
sub(r,s,[t]):对t字符串搜索r表示模式匹配的内容,并将第一个匹配内容替换为s
gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

范例:统计用户名的长度

[14:46:44 root@centos8 ~]#cut -d: -f1 /etc/passwd | awk '{print length()}'
[14:49:03 root@centos8 ~]#awk -F: '{print length($1)}' /etc/passwd

范例:替换字符串字符,替换的是第一个

[14:49:56 root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08
[14:51:08 root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
2008-08:08 08:08:08

范例:指定字符串全局替换

[14:51:20 root@centos8 ~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'
2008-08-08 08:08:08
[14:51:56 root@centos8 ~]#echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$1);print $0}'
2008-08-08 08:08:08

范例:

[14:53:32 root@centos8 ~]#netstat -tn | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
192.168.10.1 1
system('cmd')

空格是 awk 中的字符串连接符,如果 system 中需要使用 awk 中的变量可以使用空格分隔,或者说 除了 awk 的变量外其他一律用""引用起来

[14:54:58 root@centos8 ~]#awk 'BEGIN{system("hostname")}'
centos8
[14:57:28 root@centos8 ~]#awk 'BEGIN{score=100;system("echo your score is "score"")}'
your score is 100

[15:01:54 root@centos8 ~]#netstat -nt | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10){system("iptables -A INPUT -s "i" -j REJECT")}}}'

官方文档:时间函数

https://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions

systime()  当前时间到1970年1月1日的秒数
strftime() 指定时间格式

范例:

[15:07:03 root@centos8 ~]#awk 'BEGIN{print strftime("%Y-%m-%dT%H:%M")}'
2021-01-07T15:07
[15:07:05 root@centos8 ~]#awk 'BEGIN{print systime()}'
1610003233
#前一个小时
[15:18:00 root@centos8 ~]#awk 'BEGIN{print strftime("%Y-%m-%dT%H:%M",systime()-3600)}'
2021-01-07T14:21

4.14.2 自定义函数

自定义函数格式:

function name ( parameter, parameter,... )
{ statements
return expression
}

范例:

[15:10:23 root@centos8 ~]#cat func.awk
function max(x,y) {
x>y?var=x:var=y
return var
}
BEGIN{print max(a,b)}
[15:10:27 root@centos8 ~]#awk -v a=80 -v b=40 -f func.awk
80

4.15 awk 脚本

将 awk 程序写成脚本,直接调用或执行

范例:

[15:12:54 root@centos8 ~]#cat passwd.awk
#! /bin/awk -f
{if($3>=1000)print $1,$3}
[15:12:59 root@centos8 ~]#awk -F: -f passwd.awk /etc/passwd
nobody 65534
zhang 1000

[15:14:57 root@centos8 ~]#cat test.awk
#! /bin/awk -f
{if($3>=1000)print $1,$3}
[15:15:02 root@centos8 ~]#chmod +x test.awk
[15:15:05 root@centos8 ~]#./test.awk -F: /etc/passwd
nobody 65534
zhang 1000

向 awk 脚本传递参数

格式:

awkfile  var=value  var2=value2... Inputfile

注意:在 BEGIN 过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让 awk 在执行

BEGIN 之前得到变量的值。命令行中每一个指定的变量都需要一个-v 参数

范例:

[15:17:28 root@centos8 ~]#cat test2.awk
#! /bin/awk -f
{if($3>=min && $3<=max)print $1,$3}
[15:17:34 root@centos8 ~]#chmod +x test2.awk
[15:17:39 root@centos8 ~]#./test2.awk -F: min=100 max=200 /etc/passwd
systemd-resolve 193

练习:

  1. 文件 host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中
1  www.magedu.com
2  blog.magedu.com
3  study.magedu.com
4  linux.magedu.com
5  python.magedu.com
......
999 study.magedu.com

[16:04:41 root@centos8 ~]#awk -F"[ .]" '{print $2}' host_list.log >host_list.log
  1. 统计/etc/fstab 文件中每个文件系统类型出现的次数
[16:08:36 root@centos8 ~]#awk '!/^#/ && !/^$/{type[$3]++}END{for(i in type){print i,type[i]} }' /etc/fstab
swap 1
ext4 1
xfs 1
  1. 统计/etc/fstab 文件中每个单词出现的次数
[16:41:14 root@centos8 ~]#grep -oE "[[:alpha:]]+" /etc/fstab | awk '{num[$1]++}END{for(i in num)print i,num[i]}'
  1. 提取出字符串 Yd$C@M05MB%9&Bdh7dq+YVixp3vpw 中的所有数字
[16:57:25 root@centos8 ~]#echo "Yd$C@M05MB%9&Bdh7dq+YVixp3vpw" | awk -F"[^0-9 ]+" '{for(i=1;i<=NF;i++){printf "%s",$i}}'
05973
  1. 有一文件记录了 1-100000 之间随机的整数共 5000 个,存储的格式 100,50,35,89…请取出其中最大和最小的整数
#先生成文件
[16:57:36 root@centos8 ~]#awk  'BEGIN{for(i=1;i<=5000;i++){OSF=",";printf "%s,",int(rand()*1000000)}}' >num.bak
#取最大最小
[17:06:38 root@centos8 ~]#awk -F"," -v min=1000000 '{for(i=1;i<=5000;i++){if($i>max){max=$i}else if($i
  1. 解决 Dos 攻击生产案例:根据 web 日志或者或者网络连接数,监控当某个 IP 并发连接数或者短时内 PV 达到 100,即调用防火墙命令封掉对应的 IP,监控频率每隔 5 分钟。防火墙命令为:iptables -AINPUT -s IP -j REJECT
[17:25:15 root@centos8 ~]#ss -nt | awk -F"[ :]+" 'NR>1{ip[$6]++}END{for(i in ip){if(ip[i]>100){system("iptables -A INPUT -s "i" -j REJECT")}}}'

[17:30:28 root@centos8 ~]#crontab -l
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
*/5 * * * * ss -nt | awk -F"[ :]+" 'NR>1{ip[$6]++}END{for(i in ip){if(ip[i]>100){system("iptables -A INPUT -s "i" -j REJECT")}}}
  1. 将以下文件内容中 FQDN 取出并根据其进行计数从高到低排序
http://mail.magedu.com/index.html
http://www.magedu.com/test.html
http://study.magedu.com/index.html
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg
http://blog.magedu.com/20080102.html
http://www.magedu.com/images/magedu.jpg

[17:35:11 root@centos7 ~]#awk -F/ '{num[$3]++}END{for(i in num){print i,num[i]}}' a |sort -k2nr
www.magedu.com 3
blog.magedu.com 2
mail.magedu.com 1
study.magedu.com 1
  1. 将以下文本文件 awktest.txt 中 以 inode 列为标记,对 inode 列相同的 counts 列进行累加,并且统计出同一 inode 中,beginnumber 列中的最小值和 endnumber 列中的最大值
inode|beginnumber|endnumber|counts|
106|3363120000|3363129999|10000|
106|3368560000|3368579999|20000|
310|3337000000|3337000100|101|
310|3342950000|3342959999|10000|
310|3362120960|3362120961|2|
311|3313460102|3313469999|9898|
311|3313470000|3313499999|30000|
311|3362120962|3362120963|2|

[17:56:58 root@centos7 ~]#awk -F"|" 'NR>1{num[$1]+=$4;if(!begin[$1])begin[$1]=$2;else if(begin[$1]>$2)begin[$1]=$2;if(!end[$1])end[$1]=$3;else if(end[$1]<$3)end[$1]=$3}END{for(i in num)print i"|"begin[i]"|"end[i]"|"sum[i]}' awktest.txt
310|3337000000|3362120961|
311|3313460102|3362120963|
106|3363120000|3368579999|
  • awk
    5 引用 • 1 回帖
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    844 引用 • 911 回帖 • 223 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...