上一篇介绍了AWK的一些基本使用规则和一些示例,本篇介绍AWK进阶的一些知识
PATTERN 模式匹配,有点类似于sed中的地址定界,用来过滤符合要求的记录
(1)默认是空模式,匹配每一行
(2)/regular expression/ :表示过滤出符合模式的的记录,正则表达式
(3)relational expression:关系表达式,这里结果是布尔值,有“真”有“假”,只有结果为“真”才处理
这里的“真”即结果是非零或非空的表示真,其它为“假”。
例如,以:为域分隔符,对符合表达式的行进行打印。这里的意思即当$3小于等于30的行才打印
例如,以:为分隔符,$3小于等于1000并且最后一个域是字符串/bin/bash 的行,然后打印
(4)行范围定界
类似于sed,/pat1/,/pat2/。
例如,匹配第一个以h开头的行到第一个以a开头的行并打印
注意:这里不能像sed那样直接通过数字界定
(5)BGEIN/END模式
BEGIN{} 仅在开始读入记录之前执行一次
END{} 尽在文本处理完成后执行一次
[root@centos7 11:18:52 ~]#awk -F: 'BEGIN{print "username UID \n---------------------------"}$3<=1000 && $NF=="/bin/bash"{printf "%-15s%d\n",$1,$3}END{print "=======================\nend"}' /etc/passwd username UID --------------------------- root 0 zmh 1000 ======================= end
(6)progranm中常用的action
a.Expressions
b.条件控制语句:if,while等
c.组合语句
d.inpue statements
e.output statements
(7)控制语句
if(condition){statments}
if(condition){statments}else{statements}
while(condition){statements}
do{stetements}while(condition)
for(expr1;expr2;expr3){statements}
break
continue
delete array[index]
delete array
exit
控制语句
1.if-else
语法:if(condition)statement[else statement]
[root@centos7 14:50:38 ~]#awk -F: '{if($3<=50&&$3!=0)print}' /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 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 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 rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin [root@centos7 14:54:51 ~]#awk -F: '{if($3<=20){print $1,$3}else{print $1}}' /etc/passwd root 0 bin 1 daemon 2 adm 3 lp 4 sync 5 shutdown 6 halt 7 mail 8 operator 11 games 12 ftp 14 nobody systemd-network dbus polkitd abrt libstoragemgmt rpc 注意,如果if是单分支则statements不用使用{}括起来,如果是if else双分支语句那么statement需要使用{}括起来
示例,取出磁盘使用率大于指定值的磁盘
[root@centos7 15:09:30 ~]#df | awk -F% '/^\/dev\/sd/{print $1}' /dev/sda2 52403200 3876228 48526972 8 /dev/sda3 31441920 354300 31087620 2 /dev/sda1 1038336 161628 876708 16 [root@centos7 15:09:38 ~]#df | awk -F% '/^\/dev\/sd/{print $1}' | awk '{if($NF>=10)print $1,$NF}' /dev/sda1 16
2.while循环
语法:shile(condition)statement
条件为“真”进入循环;条件为“假”退出循环。如果第一次判断就是“假”那么就不会进入循环
使用场景:对一行内的多个字段逐一进行处理时使用;对数组中的各元素进行逐一处理
例如,统计/opt/httpd-2.2.34/README中每个字符出现次数并打印字符及字符长度(length()函数引用)
[root@centos7 16:09:53 usr]#awk '{i=1;while(i<NF){print $i,length($i);i++}}' /opt/httpd-2.2.34/README| sort -t' ' -k 2 -nr | uniq -c |sort -nr 37 the 3 19 of 2 18 Apache 6 17 to 2 15 and 3 13 for 3 10 The 3 9 in 2 8 a 1 7 is 2 7 be 2 7 as 2 6 you 3 6 this 4
3.do-while循环
语法:do{statment;...}while(condition) 无论真假,至少执行一次循环
示例,计算1..100整数之和
[root@centos7 16:10:13 usr]#awk 'BEGIN{sum=0;i=0;do{sum+=i;i++}while(i<=100){print sum}}' 5050 [root@centos7 16:18:31 usr]#
4.for循环
语法:for(expr1;expr2;expr3){statement;...}
遍历数组元素:for(var in array){statement}
示例,遍历grub.cfg文件,并存入word[]数组,然后遍历数组元素并答应值
[root@centos7 16:37:01 usr]#awk '{for(i=1;i<=NF;i++){word[$i]++}}END{for(j in word){print j,word[j]}}' /boot/grub2/grub.cfg insmod 14 savedefault 1 tuned_params="" 1 ro 2 --hint='hd0,msdos1' 2 boot_once=true 2 'gnulinux-3.10.0-693.el7.x86_64-advanced-6aaae4c2-65bf-40c9-a001-8ab05ccbc30d' 1 x"${feature_menuentry_id}" 1 type 1 an 1 /etc/grub.d/41_custom 2 after 1 root='hd0,msdos1' 2
示例,用不同方法 求1..1000000之和,用time计算所耗时间
[root@centos7 16:55:55 usr]#time (awk 'BEGIN{sum=0;for(i=0;i<=1000000;i++){sum+=i}print sum}') 500000500000 real 0m0.119s user 0m0.116s sys 0m0.004s [root@centos7 16:55:56 usr]#time (sum=0;for i in {1..1000000};do let sum+=$i;done;echo $sum) 500000500000 real 0m5.864s user 0m5.685s sys 0m0.174s [root@centos7 16:56:08 usr]#time (seq -s "+" 1 1000000|bc) 500000500000 real 0m0.441s user 0m0.280s sys 0m0.245s [root@centos7 16:56:47 usr]#
以上可以看出awk计算效率远比shell高
5.switch语句
语法:switch(expression){case VALUE1 or /REGEXP/:statement1;case VALUE2 or /REGEXP/:statement2;...;default:statement}
6.break和continue
示例,
[root@centos7 17:09:33 usr]#awk 'BEGIN{sum=0;for(i=0;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}' 2500 [root@centos7 17:11:25 usr]#awk 'BEGIN{sum=0;for(i=0;i<=100;i++){if(i==88)break;sum+=i}print sum,i}' 3828 88
7.next:提前结束对本行处理而直接进入下一行处理(awk自身循环读入行的循环)
示例,
[root@centos7 17:15:05 usr]#awk -F: '{if($3%2==1)next;print $1,$3,NR}' /etc/passwd root 0 1 daemon 2 3 lp 4 5 shutdown 6 7 mail 8 9 games 12 11 ftp 14 12 systemd-network 192 14 libstoragemgmt 998 18 rpc 32 19 saslauth 996 21 rtkit 172 22 nfsnobody 65534 26 geoclue 994 29 gdm 42 33 gnome-initial-setup 992 34 sshd 74 35 avahi 70 36 ntp 38 38 tcpdump 72 39 zmh 1000 40 docker 1002 42 base 1004 44 testbash 1006 46 nologin 1008 47 apache 48 49 user1 1010 50
awk数组
awk中数组都是关联数组,array[index-expression]
index-expression:
(1)可使用任意字符串;字符串要使用双引号括起来
(2)如果某数组元素事先不存在,在引用时awk会自动创建此元素并初始化为“空”
若要判断数组是否存在某元素,使用"index in array"格式进行遍历数组
示例,array[$0]事先不存在,awk自动创建并赋值为空,再!取反则值为1,表示真,然后执行默认{print $0}
[root@centos7 18:44:47 usr]#awk '!arr[$0]' /etc/fstab # # /etc/fstab # Created by anaconda on Wed Mar 28 02:08:30 2018 # Created by anaconda on Wed Mar 28 02:08:30 2018 # # Accessible filesystems, by reference, are maintained under '/dev/disk' # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # UUID=6aaae4c2-65bf-40c9-a001-8ab05ccbc30d / xfs defaults 0 0 UUID=1e010872-4180-4ce5-be38-4152a4f302e5 /boot xfs defaults 0 0 UUID=66f9e8a4-7d65-4ced-8bd9-f76e5e7b97d6 /data xfs defaults 0 0 UUID=af954884-fc86-4450-9c1d-342531c53c7b swap swap defaults 0 0 /dev/sr0 /data/mnt/6/x86_64/ iso9660 defaults 0 0 /dev/sr1 /data/mnt/7/x86_64/ iso9660 defaults 0 0 [root@centos7 18:44:53 usr]#
若要遍历数组中的每个元素,要使用for循环。for(var in array){for-body}
注意:var会遍历array的每个索引
示例,筛选以tcp开头的网络端口状态,并存储在state[]数组中,再使用for遍历数组并打印数组索引(即端口状态),和数组元素(即相同状态个数)
[root@centos7 18:44:53 usr]#netstat -tan | awk '/^tcp/{state[$NF]++}END {for(i in state) { print i,state[i]}}' LISTEN 9 ESTABLISHED 1
awk函数
数值处理函数
rand():返回0和1之间一个随机数
示例,
[root@centos7 18:57:55 usr]#awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }' 13 92 18 63 39 4 42 12 18 92
字符处理函数:
length([s]):返回指定字符串的长度
sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s
[root@centos7 18:57:56 usr]#awk 'sub(/:/,"---",$1)' /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 lp---x:4:7:lp:/var/spool/lpd:/sbin/nologin sync---x:5:0:sync:/sbin:/bin/sync [root@centos7 19:02:17 usr]#awk 'gsub(/:/,"---",$1)' /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 lp---x---4---7---lp---/var/spool/lpd---/sbin/nologin sync---x---5---0---sync---/sbin---/bin/sync
gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
split(s,array,[r]):以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…
示例,
[root@centos7 19:06:50 usr]#netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}' 192.168.30.1 1 0.0.0.0 4
自定义函数
格式:
function name ( parameter, parameter,...){
statements
return expression
}
示例:比较a,b的大小
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
awk -f fun.awk
示例,求男生(m)的平均成绩,女生(f)的平均成绩
[root@centos7 19:15:56 bin]#cat score.txt leifeng 100 m lilei 99 m limengmeng 90 f han××× 100 f [root@centos7 19:16:07 bin]#awk '{num[$NF]++;sum[$NF]+=$2 }END{for(sex in num)printf "%s:%.2f\n",sex,sum[sex]/num[sex]}' score.txt m:99.50 f:95.00 [root@centos7 19:16:09 bin]#
awk中调用shell命令
system命令,空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来。
示例,
[root@centos7 19:16:09 bin]#awk 'BEGIN{score=100; system("echo your score is " score) }' your score is 100
向awk脚本传递参数
格式:
awkfile var=value var2=value2... Inputfile
注意:在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通 过-v 参数,让awk在执行BEGIN之前得到变量的值。命令行中每一个指定的变 量都需要一个-v参数
示例,
[root@centos7 19:25:23 bin]#cat test.awk #!/bin/awk -f {if($3 >=min && $3<=max)print $1,$3} [root@centos7 19:25:26 bin]#chmod +x test.awk [root@centos7 19:25:31 bin]#test.awk -F: min=100 max=200 /etc/passwd systemd-network 192 abrt 173 rtkit 172 qemu 107 usbmuxd 113 pulse 171 [root@centos7 19:25:33 bin]#