文本处理三剑客之 awk
awk 工作原理和基本用法说明
awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会(FSF)进行开发和维护,通常也称它为 GNU AWK
有多种版本:
AWK:原先来源于 AT & T 实验室的的AWK
NAWK:New awk,AT & T 实验室的AWK的升级版
GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容
gawk:模式扫描和处理语言,可以实现下面功能
文本处理
输出格式化的文本报表
执行算数运算
执行字符串操作
格式:
awk [options] 'program' var=value file…
awk [options] -f programfile var=value file…
说明:
program通常是被放在单引号中,并可以由三种部分组成
BEGIN语句块
模式匹配的通用语句块
END语句块
常见选项:
-F “分隔符” 指明输入时用到的字段分隔符,默认的分隔符是若干个连续空白符
-v var=value 变量赋值
Program格式:
pattern{action statements;..}
pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END,正则表达式等
action statements:对数据进行处理,放在{}内指明,常见:print, printf
awk 工作过程
第一步:执行BEGIN{action;… }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{action;…}语句块BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块 pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块
分割符、域和记录
由分隔符分隔的字段(列column,域field)标记$1,$2...$n称为域标识,$0为所有域,注意:和 shell中变量$符含义不同
文件的每一行称为记录record
如果省略action,则默认执行 print $0 的操作
常用的action分类
output statements:print,printf
Expressions:算术,比较表达式等
Compound statements:组合语句
Control statements:if, while等
input statements
awk控制语句
{ statements;… } 组合语句
if(condition) {statements;…}
if(condition) {statements;…} else {statements;…}
while(conditon) {statments;…}
do {statements;…} while(condition)
for(expr1;expr2;expr3) {statements;…}
break
continue
exit
动作 print
格式
print item1, item2, ...
说明:
逗号分隔符
输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
如省略item,相当于print $0
固定字符符需要用“ ” 引起来,而变量和数字不需要
范例
[root@centos8 ~]# awk '{print "hello,awk"}'
[root@centos8 ~]# 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
[root@centos8 ~]# seq 3 | awk '{print 2*3}'
6
6
6
[root@centos8 ~]# awk -F: '{print "long"}' /etc/passwd
[root@centos8 ~]# awk -F: '{print}' /etc/passwd
[root@centos8 ~]# awk -F: '{print $0}' /etc/passwd
[root@centos8 ~]# awk -F: '{print $1,$3}' /etc/passwd
[root@centos8 ~]# awk -F: '{print $1" "$3}' /etc/passwd
[root@centos8 ~]# grep "^UUID" /etc/fstab | awk {'print $2,$3'}
/ xfs
/boot ext4
/data xfs
swap swap
面题:取出网站访问量最大的前3个IP
[root@centos8 ~]# awk '{print $1}' nginx.access.log | sort | uniq -c | sort -nr | head -n3
213 122.54.68.23
168 117.145.214.13
56 223.158.167.123
[root@centos8 ~]# awk '{print $1}' access_log | sort | uniq -c | sort -nr | head
4870 172.20.116.228
3429 172.20.116.208
2834 172.20.0.222
2613 172.20.112.14
2267 172.20.0.227
2262 172.20.116.179
2259 172.20.65.65
1565 172.20.0.76
1482 172.20.0.200
1110 172.20.28.145
面试题:取出分区利用率
[root@centos8 ~]# df | awk '{print $1,$5}'
Filesystem Use%
devtmpfs 0%
tmpfs 0%
tmpfs 2%
tmpfs 0%
/dev/sda2 3%
/dev/sda3 1%
/dev/sda1 15%
tmpfs 0%
#使用扩展的正则表达式
[root@centos8 ~]# df | awk -F"[[:space:]]+|%" '{print $5}'
Use
0
0
1
0
5
1
92
1
[root@centos8 ~]# df | awk -F'[[:space:]]+|%' '{print $1,$5}'
Filesystem Use
devtmpfs 0
tmpfs 0
tmpfs 2
tmpfs 0
/dev/sda2 3
/dev/sda3 1
/dev/sda1 15
tmpfs 0
[root@centos8 ~]# df | grep "^/dev/sd" | awk -F"[[:space:]]+|%" '{print $5}'
3
1
15
[root@centos8 ~]# df | grep '^/dev/sd' | awk -F'[[:space:]]+|%' '{print $1,$5}'
/dev/sda2 3
/dev/sda3 1
/dev/sda1 15
[root@centos8 ~]# df | awk -F"[[:space:]]+|%" '/^/dev/sd/{print $5}'
3
1
15
[root@centos8 ~]# df | awk -F'[[:space:]]+|%' '/^/dev/sd/{print $1,$5}'
/dev/sda2 3
/dev/sda3 1
/dev/sda1 15
面试题:取 ifconfig 输出结果中的IP地址
[root@centos8 ~]# ifconfig eth0 | awk '/netmask/{print $2}'
10.0.0.100
[root@centos6 ~]# ifconfig eth0 | awk -F " +|:" '/Mask/{print $4}'
10.0.0.6
[root@centos8 ~]# ifconfig eth0 | sed -nr '2s/^[0-9]+([0-9.]+) .*$/1/p'
10.0.0.100
[root@centos6 ~]# ifconfig eth0 | sed -nr '2s/^[0-9]+([0-9.]+) .*$/1/p'
10.0.0.6
面试题:文件host_list.log 如下格式,请提取”.longwang.com”前面的主机名部分并写入到回到该文件中
[root@centos8 ~]# cat host_list.log
1 www.longwang.com
2 blog.longwang.com
3 study.longwang.com
4 linux.longwang.com
5 python.longwang.com
[root@centos8 ~]# awk -F"[ .]" '{print $2}' host_list.log
www
blog
study
linux
python
[root@centos8 ~]# awk -F"[ .]" '{print $2}' host_list.log >> host_list.log
[root@centos8 ~]#cat host_list.log
1 www.longwang.com
2 blog.longwang.com
3 study.longwang.com
4 linux.longwang.com
5 python.longwang.com
www
blog
study
linux
python
awk变量
常见的内置变量
FS:输入字段分隔符,默认为空白字符,功能相当于 -F
OFS:输出字段分隔符,默认为空白字符
RS:输入记录record分隔符,指定输入时的换行符
ORS:输出记录分隔符,输出时用指定符号代替换行符
NF:字段数量
NR:记录的编号
FNR:各文件分别计数,记录的编号
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],......
awk中的变量分为:内置和自定义变量
常见的内置变量
FS:输入字段分隔符,默认为空白字符,功能相当于 -F
范例:
awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
awk -v FS=":" '{print $1FS$3}' /etc/passwd
awk –F: '{print $1,$3,$7}' /etc/passwd
S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd
[root@centos8 ~]# awk -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3
root:0
bin:1
daemon:2
[root@centos8 ~]# S=:;awk -F$S '{print $1,$3}' /etc/passwd|head -n3
root 0
bin 1
daemon 2
#-F 和 FS变量功能一样,同时使用会冲突
[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;
[root@centos8 ~]# awk -F";" -v FS=":" '{print $1FS$3}' /etc/passwd |head -n3
root:0
bin:1
daemon:2
OFS:输出字段分隔符,默认为空白字符
范例
[root@centos8 ~]# awk -v FS=':' '{print $1,$3,$7}' /etc/passwd | head -n1
root 0 /bin/bash
[root@centos8 ~]# awk -v FS=":" -v OFS=":" '{print $1,$3,$7}'
/etc/passwd|head -n1
root:0:/bin/bash
RS:输入记录record分隔符,指定输入时的换行符
范例:
awk -v RS=' ' ‘{print }’ /etc/passwd
ORS:输出记录分隔符,输出时用指定符号代替换行符
范例
awk -v RS=' ' -v ORS='###' '{print $0}' /etc/passwd
NF:字段数量
范例
#引用变量时,变量前不需加$
[root@centos8 ~]# awk -F: '{print NF}' /etc/fstab
[root@centos8 ~]# awk -F: '{print $(NF-1)}' /etc/fstab
[root@centos8 ~]# ls /misc/cd/BaseOS/Packages/*.rpm | awk -F ":" '{print $(NF-1)}' | sort | uniq -c
389 i686
208 noarch
1060 x86_64
面试题:接数最多的前3个IP
[root@centos8 ~]# awk -F " +|:" '{print $(NF-2)}' sos.log | sort | uniq -c | sort -nr | head -n3
20 223.66.64.148
16 119.23.176.118
13 182.250.65.202
[root@centos8 ~]# awk -F " +|:" '/^ESTAB/{print $(NF-2)}' sos.log | sort | uniq -c | sort -nr | head -n3
15 223.77.34.147
10 182.202.66.23
8 116.134.156.88
[root@centos8 ~]# ss -nt | grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print $(NF-2)}'
10.0.0.1
10.0.0.100
10.0.0.1
[root@centos8 ~]# ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'
Address
10.0.0.1
[root@centos8 ~]# ss -tn | awk -F: '{print $(NF-1)}' | awk '/^[0-9]/{print $NF}' | sort | uniq -c | head -n3
范例:每十分钟检查将连接数超过100个以上的IP放入黑名单拒绝访问
[root@centos8 ~]# cat deny_dos.sh
#!/bin/bash
LINK=100
while true ;do
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
done
[root@centos8 ~]# chmod +x /root/deny_dos.sh
[root@centos8 ~]# crontab -e
[root@centos8 ~]# crontab -l
*/10 * * * * /root/deny_dos.sh
范例:
[root@centos8 ~]# cat deny_dos.sh
#!/bin/bash
IPLIST=`awk -F" +|:" '/^ESTAB/{print $(NF-2)}' sos.log | sort | uniq -c | sort -nr | head -n3 | awk '{print $2}'`
for ip in $IPLIST;do
iptables -A INPUT -s $ip -j REJECT
done
NR:记录的编号
范例:
[root@centos8 ~]# awk '{print NR,$0}' /etc/issue /etc/centos-release
1 S
2 Kernel
on an m
3
4 CentOS Linux release 8.1.1911 (Core)
范例:取ifconfig输出结果中的IP地址
[root@centos8 ~]# ifconfig eth0 | awk '/netmask/{print $2}'
10.0.0.100
[root@centos8 ~]# ifconfig eth0 | awk 'NR==2{print $2}'
10.0.0.100
范例:
[root@centos8 ~]# awk -F: '{print NR}' /etc/passwd
1
2
3
.......
[root@centos8 ~]# awk -F: 'END{print NR}' /etc/passwd
57
[root@centos8 ~]# awk -F: 'BEGIN{print NR}' /etc/passwd
0
FNR:各文件分别计数,记录的编号
范例:
awk '{print FNR}' /etc/fstab /etc/inittab
[root@centos8 ~]# awk '{print NR,$0}' /etc/issue /etc/redhat-release
1 S
2 Kernel
on an m
3
4 CentOS Linux release 8.0.1905 (Core)
[root@centos8 script40]# awk '{print FNR,$0}' /etc/issue /etc/redhat-release
1 S
2 Kernel
on an m
3
1 CentOS Linux release 8.0.1905 (Core)
FILENAME:当前文件名
范例:
[root@centos8 ~]# awk '{print FILENAME}' /etc/fstab
[root@centos8 ~]# awk '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release
1 /etc/issue S
2 /etc/issue Kernel
on an m
3 /etc/issue
1 /etc/redhat-release CentOS Linux release 8.0.1905 (Core)
ARGC:命令行参数的个数
范例:
[root@centos8 ~]# awk '{print ARGC}' /etc/issue /etc/redhat-release
3
3
3
3
[root@centos8 ~]# awk 'BEGIN{print ARGC}' /etc/issue /etc/redhat-release
3
ARGV:数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],......
范例:
[root@centos8 ~]# awk 'BEGIN{print ARGV[0]}' /etc/issue /etc/redhat-release
awk
[root@centos8 ~]# awk 'BEGIN{print ARGV[1]}' /etc/issue /etc/redhat-release
/etc/issue
[root@centos8 ~]# awk 'BEGIN{print ARGV[2]}' /etc/issue /etc/redhat-release
/etc/redhat-release
[root@centos8 ~]# awk 'BEGIN{print ARGV[3]}' /etc/issue /etc/redhat-release
自定义变量(区分字符大小写)
-v var=value
在program中直接定义
范例:
awk -v test='hello gawk' '{print test}' /etc/fstab
awk -v test='hello gawk' 'BEGIN{print test}'
awk 'BEGIN{test="hello,gawk";print test}'
awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd
cat awkscript
{print script,$1,$2}
awk -F: -f awkscript script="awk" /etc/passwd