1. awk基础概念
awk是一个强大的文本分析工具,相对于grep的查找 , sed的编辑 , awk在其对数据分析并生成报告时 , 显得尤为强大。简单来说awk就是把文件逐行的读入 , 以空格为默认分隔符将每行切片 ,切开的部分再进行各种分析处理。 awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。 awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言 : AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序 , 这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表 , 还有无数其他的功能。
1.1 awk语法
基本用法:
awk [options] 'program' file…awk [options] -f programfile var=value file…awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file ...
说明 :
program通常是被放在单引号中,并可以由三种部分组成
BEGIN语句块模式匹配的通用语句块
END语句块
选项:
-F: 指明输入时用到的字段分隔符-v var=value: 自定义变量
program:
pattern{action statements;..}pattern和action:
pattern部分决定动作语句何时触发及触发事件BEGIN,ENDaction statements对数据进行处理,放在{}内指明print,printf
awk程序通常由 : BEGIN语句块、能够使用模式匹配的通用语句块 、END语句块,共3部分组成
1.2 分割符、域和记录
awk执行时 , 由分隔符分隔的字段(域,field)标记$1,$2..$n称 为域标识。$0为所有域 , 注意 : 和shell中变量$符含义不同。 文件的每一行称为记录(Record)
省略
action, 则默认执行print $0的操作
1.3 AWK工作原理
1.3.1 处理流程
第一步:执行
BEGIN{action;… }语句块中的语句第二步:从文件或标准输入(
stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这 个过程,直到文件全部被读取完毕。第三步:当读至输入流末尾时,执行
END{action;…}语句块。
1.3.2 步骤说明
BEGIN语句块在
awk开始从输入流中读取行之前被执行 , 这是一个 可选的语句块 , 比如变量初始化、打印输出表格的表头等语句通常 可以写在BEGIN语句块中END语句块在
awk从输入流中读取完所有的行之后即被执行 , 比如 打印所有行的分析结果这类信息汇总都是在END语句块中完成 , 它 也是一个可选语句块 。pattern语句块中的通用命令是最重要的部分 , 也是可选的。如果 没有提供
pattern语句块 , 则默认执行{ print } , 即打印每一个读取 到的行 ,awk读取的每一行都会执行该语句块
2. print和printf
2.1 print
格式:
print item1, item2, ...要点:
逗号分隔符
输出的各
item可以是字符串,也可以是数值 ;当前记录的字段、 变量或awk的表达式如省略
item, 相当于print $0引用的变量想要被替换不允许被引号括起来
范例 :
[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
#每一行均打印为wang,打印行数和/etc/passwd文件中行数一样
[root@centos8 ~]#awk -F: '{print "wang"}' /etc/passwd
#打印/etc/passwd中的行,类似于cat /etc/passwd
[root@centos8 ~]# awk -F: '{print}' /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
adm:x:3:4:adm:/var/adm:/sbin/nologin
...
[root@centos8 ~]#awk -F: '{print $0}' /etc/passwd
#以:为分隔符,并打印/etc/passwd中的第一列
[root@centos8 ~]#awk -F: '{print $1}' /etc/passwd
root
bin
daemon
adm
lp
...
#以:为分隔符,打印第1列和第3列,中间有一个制表符
[root@localhost ~]# awk -F: '{print $1"\t"$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
...
[root@centos8 ~]#grep "^UUID" /etc/fstab |awk {'print $2,$3'}
/ xfs
/boot ext4
/data xfs
swap swap面试题:取出网站访问量最大的前3个IP
[root@VM_0_10_centos logs]# 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
[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" +|%" '{print $5}' #需要注意这命令和上面那个命令一样,只是将[[:space:]]替换为"␣"这种单纯的空格键,命令中的空白字符实际是生效的
[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}'
51
92
[root@centos8 ~]#df | grep '^/dev/sd'| awk -F'[[:space:]]+|%' '{print $1,$5}'
/dev/sda2 3
/dev/sda3 1
/dev/sda1 15面试题:取 ifconfifig 输出结果中的IP地址
[root@centos8 ~]#ifconfig eth0 | awk '/netmask/{print $2}'
10.0.0.8
[root@centos6 ~]#ifconfig eth0 |awk -F " +|:" '/Mask/{print $4}'
10.0.0.6
[root@centos8 ~]#ifconfig eth0| sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'
10.0.0.8
[root@centos6 ~]#ifconfig eth0| sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'
10.0.0.6面试题 : 文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中
[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
[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.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
www
blog
study
linux
python2.2 printf
格式 :
printf “FORMAT”, item1, item2, ...使用注意点 :
必须指定
FORMAT不会自动换行,需要显式给出换行控制符,
\nFORMAT中需要分别为后面每个item指定格式符
格式符:与item一一对应
%c: 显示字符的ASCII码%d,%i: 显示十进制整数%e,%E:显示科学计数法数值%f:显示为浮点数%g,%G:以科学计数法或浮点形式显示数值%s:显示字符串%u:无符号整数%%: 显示%自身
修饰符
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f-: 左对齐(默认右对齐)%-15s+:显示数值的正负符号%+d
awk -F: '{printf "%s",$1}' /etc/passwd
awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F: '{printf "Username: %s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %15s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %-15s,UID:%d\n",$1,$3}' /etc/passwd 3. awk中的变量
3.1 内置变量
FS:(
Filed Separator)输入字段分隔符,默认为空白字符[root@centos8 ~]# awk -v FS=':' '{print $1,FS,$3}' /etc/passwd | head -n 3 root : 0 bin : 1 daemon : 2 [root@centos8 ~]# awk -v FS=':' '{print $1FS$3}' /etc/passwd | head -n 3 root:0 bin:1 daemon:2 [root@centos8 ~]# awk -v FS=':' '{print $1,$3,$7}' /etc/passwd | head -n 3 root 0 /bin/bash bin 1 /sbin/nologin daemon 2 /sbin/nologin [root@centos8 ~]# S=:;awk -F$S '{print $1,$3}' /etc/passwd | head -n 3 root 0 bin 1 daemon 2 # -F 和FS变量功能一样,同时使用会冲突 [root@centos8 ~]# awk -v FS=":" -F";" '{print $1FS$3}' /etc/passwd | head -n 3 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 -n 3 root:0 bin:1 daemon:2 OFS:(
Output Filed Separator)输出字段分隔符,默认为空白字符[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/bashRS:(
Record separator)输入记录分隔符,指定输入时的换行符[root@centos8 tmp]# cat /root/test 1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com [root@centos8 tmp]# #可以看到当读到最后一行时,即使文本中没有换行符之后还是会添加一个换行符 [root@centos8 ~]# awk -v RS=' ' '{print }' /root/test 1 www.magedu.com 2 blog.magedu.com 3 study.magedu.com 4 linux.magedu.com 5 python.magedu.com # 1 单独为一行,之后www.magedu.com + 换行符 +2单独为一行,以此类推ORS:(
Outpput Record Seperator)输出记录分隔符,输出时用指定符号代替换行符[root@centos8 ~]# awk -v RS=' ' -v ORS='###' '{print $0}' /root/test 1###www.magedu.com 2###blog.magedu.com 3###study.magedu.com 4###linux.magedu.com 5###python.magedu.com ###[root@centos8 ~]#NF:(
Number of Filed)字段数量#引用变量时,变量前不需加$ [root@centos8 ~]# awk -F: '{print NF}' /etc/fstab 0 1 1 3 1 1 1 1 1 1 1 1 1 1 [root@centos8 ~]# awk -F: '{print NF}' /etc/passwd 7 7 7 7 ... #打印各个用户的家目录信息 [root@centos8 ~]# awk -F: '{print $(NF-1)}' /etc/passwd /root /bin /sbin /var/adm /var/spool/lpd /sbin /sbin /sbin /var/spool/mail ...面试题 : 显示连接数最多的前3个IP
[root@centos8 ~]#awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq - c|sort -nr|head -n3 12 223.88.255.148 10 183.202.63.36 9 117.152.155.119 [root@centos8 ~]#ss -nt |grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print $(NF- 2)}' 10.0.0.1 10.0.0.7 10.0.0.1 [root@centos8 ~]#ss -nt |awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}' [root@centos8 ~]#ss -nt|awk -F: '{print $(NF-1)}' |awk '/^[0-9]/{print $NF}'| sort |uniq -c |head -n 3范例 : 每十分钟检查将连接数超过100个以上的IP放入黑名单拒绝访问
[root@centos8 ~]#cat deny_dos.sh 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 ~]#cat deny_dos.sh IPLIST=`awk -F" +|:" '/^ESTAB/{print $(NF-2)}' ss.log |sort |uniq -c|sort - nr|head -3|awk '{print $2}'` for ip in $IPLIST;do iptables -A INPUT -s $ip -j REJECT doneNR : (
Number of Record)记录的编号 (行号)范例 :
[root@centos8 ~]# awk '{print NR,$0}' /etc/issue 1 \S 2 Kernel \r on an \m 3 [root@centos8 ~]# awk '{print NR}' /etc/passwd 1 2 3 4 5 6 ... [root@centos8 ~]# awk -F: 'BEGIN{print NR}' /etc/passwd 0 [root@centos8 ~]# awk -F: 'END{print NR}' /etc/passwd 45范例 : 取
ifconfig输出结果中的IP地址[root@centos8 ~]#ifconfig eth0 | awk '/netmask/{print $2}' 10.0.0.8 [root@centos8 ~]#ifconfig eth0 | awk 'NR==2{print $2}' 10.0.0.8FNR:分别打印所有文件的行号(各文件分别计数,记录号 )
[root@centos8 ~]# awk '{print FNR,$0}' /etc/issue /etc/centos-release 1 \S 2 Kernel \r on an \m 3 1 CentOS Linux release 8.2.2004 (Core) [root@centos8 ~]# 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)FILENAME:当前文件名
[root@centos8 ~]# awk '{print FILENAME}' /etc/issue /etc/issue /etc/issue /etc/issue [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)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 3ARGV:数组,保存的是命令行所给定的各参数 ,每一个参数:
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 [root@centos8 ~]#
引用内置变量不用$
3.2 自定义变量
自定义变量(区分字符大小写)
-v var=value在
program中直接定义
示例:
[root@centos8 ~]# awk -v test='hello gawk' '{print test}' /etc/issue
hello gawk
hello gawk
hello gawk
[root@centos8 ~]# awk -v test='hello gawk' 'BEGIN{print test}'
hello gawk
[root@centos8 ~]# awk 'BEGIN{test="hello,gawk";print test}'
hello,gawk
[root@centos8 ~]# awk -F':' '{sex="male";print $1,sex,age;age=18}' /etc/passwd
root male
bin male 18
daemon male 18
adm male 18
lp male 18
...
[root@centos8 ~]# sep=':';awk -v FS="$sep" '{print $1,FS,$3}' /etc/passwd
root : 0
bin : 1
daemon : 2
adm : 3
lp : 4
sync : 5
...
[root@centos8 ~]# sep=':';awk -F "$sep" '{print $1,":",$3}' /etc/passwd
root : 0
bin : 1
daemon : 2
adm : 3
...
[root@centos8 ~]# awk -v sep=":" -F: '{print $1sep$3}' /etc/passwd
root:0
bin:1
daemon:2
adm:3
...
[root@centos8 ~]# awk -v user="username" -v uuid="uuid" -F: '{print user":"$1"\t"uuid":"$3}' /etc/passwd
username:root uuid:0
username:bin uuid:1
...
[root@centos8 ~]# u="user";awk -v username="$u" -F: '{uid="uid";print username":"$1"\t"uid":"$3}' /etc/passwd
user:root uid:0
user:bin uid:1
user:daemon uid:2
user:adm uid:3
...
# 可以调用系统中本身的变量
[root@centos8 ~]# cat awkscript
{print script,$1,$2}
[root@centos8 ~]# awk -F: -f awkscript script="awk" /etc/passwd
awk root x
awk bin x
awk daemon x
awk adm x
...4. 操作符
算术操作符
x+y,x-y,x*y,x/y,x^y,x%y-x: 转换为负数+x: 转换为数值
字符串操作符:没有符号的操作符,字符串连接
赋值操作符:
=,+=,-=,*=,/=,%=,^=,++,--[root@centos8 ~]# awk 'BEGIN{i=0;print i++,i}' 0 1 [root@centos8 ~]# awk 'BEGIN{i=0;print ++i,i}' 1 1 [root@centos8 ~]# awk -v n=0 '!n++' /etc/passwd root:x:0:0:root:/root:/bin/bash #只打印第一行 [root@centos8 ~]# awk -v n=0 '!n++{print n}' /etc/passwd 1 [root@centos8 ~]# awk -v n=0 '!++n' /etc/passwd [root@centos8 ~]# #没有输出结果 [root@centos8 ~]# awk -v n=-1 '!++n' /etc/passwd root:x:0:0:root:/root:/bin/bash比较操作符:
==,!=,>,>=,<,<=范例
[root@centos8 ~]# awk 'NR==2' /etc/issue Kernel \r on an \m [root@centos8 ~]# awk -F: '$3>=1000' /etc/passwd nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin xiong:x:1000:1000:xiong:/home/xiong:/bin/bash范例 : 取奇,偶数行
[root@centos8 ~]# seq 10 | awk 'NR%2==0' 2 4 6 8 10 [root@centos8 ~]# seq 10 | awk 'NR%2==1' 1 3 5 7 9 [root@centos8 ~]# seq 10 | awk 'NR%2!=0' 1 3 5 7 9模式匹配符:
~:左边是否和右边匹配包含!~:是否不匹配
[root@centos8 ~]# awk -F: '$0 ~ /^root/{print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@centos8 ~]# awk -F: '$0 ~ "^root"{print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@centos8 ~]# awk -F: '$0 !~ "^root"{print $0}' /etc/passwd bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin ... [root@centos8 ~]# awk '/root/' /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@centos8 ~]#df | awk -F"[[:space:]]+|%" '$0 ~ /^\/dev\/sd/{print $5}' 5 1 92 [root@centos8 ~]# ifconfig ens33 | awk 'NR==2{print $2}' 192.168.74.128逻辑操作符:与&&,或||,非!
范例
[root@centos8 ~]# awk 'BEGIN{print i}' [root@centos8 ~]# awk 'BEGIN{print !i}' 1 [root@centos8 ~]# awk -v i=10 'BEGIN{print !i}' 0 [root@centos8 ~]# awk -v i=-3 'BEGIN{print !i}' 0 [root@centos8 ~]# awk -v i=0 'BEGIN{print !i}' 1 [root@centos8 ~]# awk -v i=abc 'BEGIN{print !i}' 0 [root@centos8 ~]# awk -v i='' 'BEGIN{print !i}' 1范例
awk –F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd awk -F: '!($3==0) {print $1}' /etc/passwd awk -F: '!($3>=500) {print $3}' /etc/passwd条件表达式(三目表达式):
selector?if-true-expression:if-false-expressionawk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
5. Program
格式 :
awk [options] 'program' file…program :
pattern{action statements;..}pattern和action:
pattern部分决定动作语句何时触发及触发事件BEGIN,ENDaction statements对数据进行处理,放在{}内指明print,printf
5.1 Program:PATTERN
PATTERN: 根据pattern条件 , 过滤匹配的行,再做处理
如果未指定:空模式,匹配每一行
/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来[root@centos8 ~]# awk '/^UUID/{print $1}' /etc/fstab UUID=16ab6947-566e-40c3-94f7-ce21c0404cb1 [root@centos8 ~]# awk '!/^UUID/{print $1}' /etc/fstab # # # # # # # # # # /dev/mapper/cl-root /dev/mapper/cl-swap [root@centos8 ~]#df | awk '/^\/dev\/sd/' /dev/sda2 104806400 4935924 99870476 5% / /dev/sda3 52403200 398876 52004324 1% /data /dev/sda1 999320 848572 81936 92% /bootrelational expression: 关系表达式,结果为“真”才会被处理真:结果为非0值,非空字符串
假:结果为空字符串或0值
范例
[root@centos8 ~]# awk '!1' /etc/issue #没有输出结果 [root@centos8 ~]# awk '0' /etc/issue [root@centos8 ~]# awk '!0' /etc/issue \S Kernel \r on an \m [root@centos8 ~]# awk '1' /etc/issue \S Kernel \r on an \m [root@centos8 ~]# awk '""' /etc/issue [root@centos8 ~]# awk -v magedu="" 'magedu' /etc/issue [root@centos8 ~]# awk -v magedu="0" 'magedu' /etc/issue [root@centos8 ~]# awk -v magedu=0 'magedu' /etc/issue [root@centos8 ~]# awk 'magedu' /etc/issue [root@centos8 ~]# awk -v magedu=wang 'magedu' /etc/issue \S Kernel \r on an \m [root@centos8 ~]# awk '"0"' /etc/issue \S Kernel \r on an \m范例 :
[root@centos8 ~]# awk -F: 'i=1;j=1{print i,j}' /etc/issue \S 1 1 Kernel \r on an \m 1 1 1 1 [root@centos8 ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd nobody 65534 xiong 1000 [root@centos8 ~]# awk -F: '$3<1000{print $1,$3}' /etc/passwd root 0 bin 1 daemon 2 adm 3 ... [root@centos8 ~]# awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd root /bin/bash xiong /bin/bash [root@centos8 ~]# awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd root /bin/bash xiong /bin/bash
line ranges:行范围startline,endline:/pat1/,/pat2/不支持直接给出数字格式
awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwdawk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
BEGIN/END模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
函数调用:
function_name(argu1, argu2, ...)
[root@localhost ~]# awk -F: 'i=1;j=1{print i,j}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
1 1
bin:x:1:1:bin:/bin:/sbin/nologin
1 1
daemon:x:2:2:daemon:/sbin:/sbin/nologin
1 1
adm:x:3:4:adm:/var/adm:/sbin/nologin
1 1
[root@localhost ~]#awk '!0' /etc/passwd
#打印该文件中的文件内容
[root@localhost ~]#awk '!1' /etc/passwd
#不打印该文件中的文件内容
[root@localhost ~]awk –F: '$3>=1000{print $1,$3}' /etc/passwd
#打印UID大于1000以上的用户信息
[root@localhost ~]awk -F: '$3<1000{print $1,$3}' /etc/passwd
#打印UID小于1000以下的用户信息
[root@localhost ~]awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
#打印登陆shell为/bin/bash的用户信息
[root@localhost ~]awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
#打印登陆shell为/bin/bash的用户信息
[root@localhost ~]awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "end file"}' /etc/passwd
[root@localhost ~]awk -F : '{print "USER USERID“;print $1":"$3} END{print "end file"}' /etc/passwd
[root@localhost ~]awk -F: 'BEGIN{print " USER UID \n--------------- "}{print $1,$3}' /etc/passwd
[root@localhost ~]awk -F: 'BEGIN{print " USER UID \n--------------- "}{print $1,$3}'END{print "=============="} /etc/passwd
[root@localhost ~]seq 10 |awk 'i=0'
#不打印内容
[root@localhost ~]seq 10 |awk 'i=1'
#打印1至10
[root@localhost ~]seq 10 | awk 'i=!i'
#由于i最开始没有复制, 默认为null 。输出奇数行
[root@localhost ~]seq 10 | awk '{i=!i;print i}'
[root@localhost ~]seq 10 | awk '!(i=!i)'
#输出偶数行
[root@localhost ~]seq 10 |awk -v i=1 'i=!i'
#输出偶数行
[root@localhost ~]seq 10 | sed -n '1~2p'
[root@localhost ~]seq 10 | sed -n '2~2p'
[root@localhost ~]df | awk -F "[ %]*" '/^\/dev\/sd/{if($(NF-2)>20)print $1,$5}'
#分隔符号同样可以实现正则表达式5.2 Program:ACTION
常用的action分类
Expressions : 算术,比较表达式等
Control statements : if, while等
Compound statements : 组合语句
input statements
output statements : print等
常用控制语句
{ statements;… }组合语句if(condition) {statements;…}if(condition) {statements;…} else {statements;…}while(conditon) {statments;…}do {statements;…} while(condition)for(expr1;expr2;expr3) {statements;…}breakcontinuedelete array[index]delete arrayexit
5.2.1 控制语句if-else
用法:
if(condition){statement;…}[else statement]if(condition1){statement1}else if(condition2){statement2} else{statement3}
使用场景 :对awk取得的整行或某个字段做条件判断
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or Sysuser: %s\n",$1}' /etc/passwd
df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=80{print $1,$5}'
awk 'BEGIN{ test=100;if(test>90){print "very good"} else if(test>60){ print "good"}else{print "no pass"}}' 5.2.2 控制语句 : while循环
语法:
while(condition){statement;…}条件真,进入循环;条件假,退出循环 。
使用场景:
对一行内的多个字段逐一类似处理时使用
对数组中的各元素逐一处理时使用
示例
#内置函数length()返回字符数,而非字节数 [root@centos8 ~]#awk 'BEGIN{print length("hello")}' 5 [root@centos8 ~]#awk 'BEGIN{print length("马哥教育")}' 4 [root@centos7 ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i); i++}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13 linux16 7 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13 [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-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 crashkernel=auto 16 net.ifnames=0 13 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 crashkernel=auto 16 net.ifnames=0 13 [root@centos8 ~]#awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print total}' 5050
5.2.3 控制语句 : do-while循环
语法:
do {statement;…}while(condition)意义:无论真假,至少执行一次循环体
示例:
[root@centos8 ~]#awk 'BEGIN{ total=0;i=1;do{ total+=i;i++;}while(i<=100);print total}' 5050
5.2.4 控制语句 : for循环
语法 :
for(expr1;expr2;expr3) {statement;…}常见用法:
for(variable assignment;condition;iteration process) {for-body}特殊用法:能够遍历数组中的元素
for(var in array) {for-body}范例 :
[root@centos8 ~]#awk 'BEGIN{total=0;for(i=1;i<=100;i++){total+=i};print total}' 5050 [root@centos7 ~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-1062.el7.x86_64 31 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13 linux16 7 /vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50 root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46 ro 2 crashkernel=auto 16 rhgb 4 quiet 5 net.ifnames=0 13性能比较 :
[root@centos8 ~]# time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}') 50005000 real 0m0.005s user 0m0.003s sys 0m0.002s [root@centos8 ~]# time (total=0;for i in {1..10000};do total=$(($total+i));done;echo $total) 50005000 real 0m0.055s user 0m0.052s sys 0m0.002s [root@centos8 ~]# time (for ((i=0;i<=10000;i++));do let total+=i;done;echo $total) 50005000 real 0m0.065s user 0m0.063s sys 0m0.000s [root@centos8 ~]# time (seq –s ”+” 10000|bc) seq: invalid floating point argument: ‘–s’ Try 'seq --help' for more information. real 0m0.014s user 0m0.002s sys 0m0.009s
5.2.5 控制语句 : switch语句
语法:
switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}
5.2.6 控制语句 : break,continue 和next
break
退出当前循环
break [n]
continue
停止此次循环, 进入下一次的循环
continue [n]
next
提前结束对本行处理而直接进入下一行处理(awk自身循环)
[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++)
{if(i%2==0)continue;sum+=i}print sum}'
2500
[root@centos8 ~]#awk 'BEGIN{sum=0;for(i=1;i<=100;i++)
{if(i==50)break;sum+=i}print sum}'
1225
[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
gluster 996
rtkit 172
rpc 32
chrony 994
saslauth 992
clevis 984
pegasus 66
colord 982
setroubleshoot 980
gdm 42
gnome-initial-setup 978
sshd 74
avahi 70
tcpdump 72
wang 10005.3 awk数组
awk默认使用的是关联数组:array[index-expression] ,在awk中使用关联数组不需要像shell中提前定义。
格式 :
array[index-expression]范例 :
weekdays["mon"]="Monday"index-expression:可使用任意字符串;字符串要使用双引号括起来
如果某数组元素事先不存在,在引用时 ,
awk会自动创建 此元素,并将其值初始化为“空串”若要判断数组中是否存在某元素,要使用
index in array格式进行遍历
若要遍历数组中的每个元素,要使用for循环
for(var in array) {for-body}注意:var会遍历array的每个索引
示例
[root@centos8 ~]#awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}' Monday [root@centos8 ~]# cat dupfile Sunday Monday Tuesday Tuesday Wednesday [root@centos8 ~]# awk '!arr[$0]++' dupfile Sunday Monday Tuesday Wednesday [root@centos8 ~]# awk '{!line[$0]++;print $0, line[$0]}' dupfile Sunday 1 Monday 1 Tuesday 1 Tuesday 2 Wednesday 1awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"] ="Tuesday";for(i in weekdays) {print weekdays[i]}}' netstat -tan | awk '/^tcp/{state[$NF]++}END {for(i in state) { print i,state[i]}}' awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log awk '{ip[$1]++}END{for(i in ip)if(ip[i]>1000)print i}' /var/log/httpd/access_log |while read line ;do >iptables -A INPUT -s $line -j REJECT >done ss -nt | awk -F "[ :]+" '/ESTAB/{print $(NF-2)}' | sort | uniq -c | sort -nr | head -n3 awk '{for(i=1;i<=NF;i++)word[$i]++}END{for(i in word)print word[i],i}' /etc/profile | sort -nr
5.4 awk函数
5.4.1 内置函数
数值处理:
rand(): 返回0和1之间一个随机数awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
rand()函数使用时,需要与函数srand()一起使用时才会有产生随机数的效果,如果单纯的使用rand()函数,只会产生固定的值0.237788 使用awk 'BEGIN{srand();i=rand();print int(i*100)}'生成0-100之前的随机数。
字符串处理:
length([s]):返回指定字符串的长度sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为secho "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换 为s所表示的内容echo "2008:08:08 08:08:08" | awk ‘gsub(/:/,"-",$0)'
split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存 至array所表示的数组中,第一个索引值为1,第二个索引值为2,…netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++} END{for (i in count) {print i,count[i]}}'
可以参考链接了解更多的awk内置函数:https://www.cnblogs.com/f-ck-need-u/p/7509812.html#9-awk-
5.4.2 自定义函数
自定义函数主要用于
ACTION字段
格式:
function name ( parameter, parameter, ... )
{
statements
return expression
} 示例:
cat fun.awk
function max(v1,v2) {
v1>v2?var=v1:var=v2
return var
}
BEGIN{a=3;b=2;print max(a,b)}
awk –f fun.awk 5.5 awk中调用shell命令
在awk中如果希望引用shell命令 , 需要使用system命令来调用对应命令。 空格是awk中的字符串连接符 , 如果system中需要使用awk中 的变量可以使用空格分隔 , 或者说除了awk的变量外其他一律 用""引用起来。
awk BEGIN'{system("hostname") }'
awk 'BEGIN{score=100; system("echo your score is " score) }'
[root@localhost ~]# awk -v string=1 BEGIN'{system("echo" string ) }'
sh: echo1: command not found
[root@localhost ~]# awk -v string=1 BEGIN'{system("echo " string ) }'
1
# 需要注意的时, 如果echo后面没有空格,运行命令时默认会将echo 与string变量合并成一个值,即echo1 认为会错误的。所以使用echo命令需要在后面加空格
[root@localhost ~]# ss -nt |awk -F "[ :]+" '/^ESATB/{IP[$(NF-2)]++}END{for( i in IP ){if(IP[i] > 3 )system("REJECT")}}'6. awk脚本
将
awk程序写成脚本 , 直接调用或执行 需要注意的是 , 如果直接写成awk脚本 , 需要将shebang写成#/bin/awk -f
示例
cat f1.awk
{if($3>=1000)print $1,$3}
awk -F: -f f1.awk /etc/passwd
----------------------------------------------------------------------
cat f2.awk
#!/bin/awk –f
#this is a awk script
{if($3>=1000)print $1,$3}
chmod +x f2.awk
f2.awk –F: /etc/passwd 6.1 向awk脚本传递参数
awk很重要且必备的能力是接受外界的变量 , 例如shell中的变量 , shell中命令执行的结果 , 或者是在开始执行awk前应该初始化的变量。 例如 , 在shell中定义一个变量name , 传递给awk使用。
awk -v awk_name="$name" 'BEGIN{print awk_name}'
Ma longshuai有三种方式可以向
awk传递变量:直接使用赋值语句
使用
-v选项传递通过参数数组`ARGV``的方式
6.1.1 第一种:直接使用赋值语句
将待传递变量当作文件名被awk解析。awk识别后发现是赋值语句 , 就认为其是变量传递。变量赋值语句必须定义awk program之后。此法定义的变量不可在BEGIN中使用 , 因为它是被当成文件解析的 , 只有在需要读取主输入文件的时候才会被解析。
awk 'BEGIN{}PATTERN{print var1,var2,var3}' var1=value1 var2=value2 file1 var3=value3 var1=value4 file2 在上面的语句中 , 当awk执行完BEGIN程序后 , 准备读取主输入 , 于是开始解析program后的输入文件。解析时发现 , var1和var2都是赋值语句 , 于是当成变量处理 , 当读取到file1时 , 发现只有一个参数 , 则当作输入文件 , 于是开始处理该文件。在处理file1时 , var1和var2都是有效的 , 但var3还未赋值 , 因此var3无效。当处理完file1后 , 继续解析下一个主输入文件 , 此时var3被赋值 , 并开始处理file2。在处理file2时 , var1、var2和var3都是有效的 , 但var1被新值覆盖。 同样,可以将前面PROGRAM部分写进文件中 。
格式:
awkfile var=value var2=value2... Inputfile
注意:在BEGIN过程中不可用。直到首行输入完成以后,变 量才可用。可以通过-v 参数,让awk在执行BEGIN之前得到 变量的值。命令行中每一个指定的变量都需要一个-v参数
[root@localhost ~]# cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
[root@localhost ~]# chmod +x test.awk
[root@localhost ~]# test.awk -F: min=100 max=200 /etc/passwd 此外 , 还可以将shell命令的结果赋值给这些预定义变量。如下展示了几种变量定义的方式:
[root@localhost ~]#name="Ma longshuai"
[root@localhost ~]# awk '{ print var1,var2,var3}' OFS=":" var1="$name" var2="`echo Ma longshuai2`" var3="Ma longshuai3" var4=Malongshuai4 /root/filename
Ma longshuai:Ma longshuai2:Ma longshuai3
[root@localhost ~]# awk '{ print var1,var2,var3}' OFS=":" var1=$name var2="`echo Ma longshuai2`" var3="Ma longshuai3" var4=Malongshuai4 /root/filename
Ma::
Ma:Ma longshuai2:Ma longshuai3 不仅可以定义普通变量 , 还可以定义内置变量(如上OFS)。注意加引号的方式 : 为了安全,应该对所有赋值语句的value部分加上双引号 , 除非所赋的值不包含特殊字符。所以 , 如果上面的var1赋值语句写成var1=$name ,将被awk解析成var1=Ma longshuai,于是var1的值为Ma , 主输入文件为longshuai。
6.1.2 第二种 : 使用-v选项传递
使用-v选项传递。变量赋值语句必须定义在awk program之前。这种方法定义的变量可以在BEGIN程序中使用。 除了定义在program之前,定义方式同上。每定义一个变量,都需要使用一个-v选项。如:
name="Ma longshuai"
awk -v OFS=":" -v var1="$name" -v var2="`echo Ma longshuai2`" -v var3="Ma longshuai3" 'program' filename6.1.3 第三种 : 通过参数数组ARGV的方式。
ARGV是内置的数组变量。awk内部会将命令行切分 , 并按规则将各参数存放到ARGV数组中 , 数组下标从0开始 , 这是awk中唯一下标从0开始的数组。在存放到ARGV时 ,所有的选项和program会被忽略。 每存储一个数组变量,特殊变量ARGC的值增加1。因此ARGC的值代表的是参数的个数。所以 , 数组变量从ARGV[0]到ARGV[ARGC-1]。 可使用类似下面的循环来遍历ARGV数组。
[root@localhost ~]# awk -F "\t" -v var1="value1" 'BEGIN{
> for(i=0;i<ARGC;++i){
> print "ARGV[" i "]: " ARGV[i]
> }
> print "ARGC: " ARGC
> }' "a" "b" "v=1" file
ARGV[0]: awk
ARGV[1]: a
ARGV[2]: b
ARGV[3]: v=1
ARGV[4]: file
ARGC: 5
注意,
ARGV[0]存储的是awk命令 ,-F和-v选项都没有存储到ARGV中。
ARGC和ARGV数组变量的值都可以手动修改。命令行分割存储完成之后 , 开始处理BEGIN , 再处理主循环输入。因此,在BEGIN中修改ARGV中输入文件对应的值 , 可以改变awk所读取的输入文件 , 若将其设置为空 , 则该数组变量直接被跳过 , 也就不再读取该输入文件。
需要注意的是 , 当增加ARGV元素时 , 必须同时递增ARGC的值 , 因为awk是根据AGRC来读取ARGV的。同理 , 只增加ARGC的值 , 将导致新建ARGV数组元素 , 且这些新元素的值为空。也因此 , 如果减小ARGC的值 , 将导致无法访问超出ARGC-1边界的ARGV元素。
参考链接 :