awk命令高级应用(数组、函数及控制语句)

一、简介 awk命令作为一种编程语言,其用法也包括了一些高级应用如控制语句、函数及数组等。这些高级应用都让awk命令更加强大,应用更加广泛,使得其能满足更多复杂的需求。
二、awk命令的数组 【awk命令高级应用(数组、函数及控制语句)】awk命令也可以结合数组来完成一系列的工作,其数组格式为:array[index-expression],其中index-expression可使用任意字符串,字符串要使用双引号括起来;如果数组中的元素事先不存在,在引用时,awk命令会自动创建此元素,并将其值初始化为“空串”。
若需要对数组进行判断是否存在某个元素,可以以index in array格式进行判断确认,index会遍历数组array中的索引,如:

[root@localhost tmp]# awk -v i=mon 'BEGIN{weekdays["mon"]="monday"; weekdays["tue"]="tuesday"; if(i in weekdays) print weekdays[i]}' monday

三、awk命令的函数 awk命令也支持函数的编写,通常来说用得较多的为awk命令的几个内置函数,其分别为:
rand():返回0和1之间一个随机数,单独使用rand()函数的话只会随机生成一次,后续多次执行也是生成同一批次的随机数;
srand([expr]):使用expr生成随机数,如果不指定expr,默认使用当前时间生成随机数;先执行srand(),在执行rand(),能让刷新rand()输出的随机数;
int(expr):将数值取整;
length([s]):返回指定字符串的长度;
sub(r,s,[t]):以r表示的模式来查找t所表示的字符串中匹配的内容,并将其第一次出现的匹配内容替换为s的内容;
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符串中匹配的内容,并将所有的匹配内容替换为s的内容;
split(s,a[,r]):以r为分隔符切割字符串s,并将切割后的结果保存至a所表示的数组中;
awk命令也支持函数的自定义,其格式为:
function function_name(argument1,argument2,...)
{
function_body
}
上述中,function_name为用户定义的函数的名称,函数通常可以接受以逗号分割的多个arguments,参数是可选的;通常来说一个function_body是由一个或多个语句组成的。
如下例子,为定义一个函数,打印UID大于100的用户名及用户UID:
[root@localhost ~]# awk -F: 'function test(a,b){if(b>100) printf "User:%-20s UID:%-10s\n",a,b}; test($1,$3)' /etc/passwd User:avahi-autoipdUID:170 User:systemd-bus-proxyUID:999 User:systemd-networkUID:998 User:polkitdUID:997 User:charlieUID:1000

四、awk命令的控制语句 简介中提及awk命令是一种编程语言,那么作为一种编程语言,其也有相应的控制语句。其控制语句的类型与bash shell编程的类似,包括:if-else、while、for、switch、break、continue和next。其中break、continue用于跳出循环及跳出本次循环,next用于提前结束当前行的处理,直接进入下一行。
下面重点介绍下其他的控制语句。
  • if-else
    awk命令的if-else语句通常用于对匹配的行或字段做条件判断选择,其格式通常为:
awk [options] '{if (condition) {statements} [else {statements}] }' /PATH/TO/SOMEFILE
如:
判断用户的UID,如果小于1000,则输出说明该用户为系统用户,大于或等于1000则输出说明该用户为普通用户:
[root@localhost ~]# awk -F: '{if ( $3<1000 ) { print $1,"is system user."} else {print $1,"is common user."}}' /etc/passwd root is system user. bin is system user. daemon is system user. adm is system user. lp is system user. sync is system user. shutdown is system user. halt is system user. mail is system user. operator is system user. games is system user. ftp is system user. nobody is system user. avahi-autoipd is system user. systemd-bus-proxy is system user. systemd-network is system user. dbus is system user. polkitd is system user. tss is system user. postfix is system user. sshd is system user. charlie is common user.

  • while循环
    awk命令的while循环常用于对一行内的多个字段进行逐个处理或者对数组中的各元素进行逐一处理。其格式为:
awk '[/PATTERN/]{while(condition) {statements}}' /PATH/TO/SOMEFILE
如:统计字段的长度:
[root@localhost ~]# awk -F: '/bash$/{i=1; while(i<=NF) {printf "%s的字段长度为:%-5s\n",$i,length($i); i++}}' /etc/passwd root的字段长度为:4 x的字段长度为:1 0的字段长度为:1 0的字段长度为:1 root的字段长度为:4 /root的字段长度为:5 /bin/bash的字段长度为:9 amandabackup的字段长度为:12 x的字段长度为:1 33的字段长度为:2 6的字段长度为:1 Amanda user的字段长度为:11 /var/lib/amanda的字段长度为:15 /bin/bash的字段长度为:9 postgres的字段长度为:8 x的字段长度为:1 26的字段长度为:2 26的字段长度为:2

  • for循环
    awk命令的for 循环常用于进行累加或遍历操作,如累计某个关键字出现次数、遍历数组等等。其常见的格式为:
awk '/PATTERN/{for(变量赋值; 判断条件; 条件变化) {循环体}}' /PATH/TO/SOMEFILE
如打印匹配行的字段长度:
[root@localhost ~]# awk -F: '/^root/{for(i=1; i<=NF; i++) printf "The length of %s is %s\n",$i,length($i)}' /etc/passwd The length of root is 4 The length of x is 1 The length of 0 is 1 The length of 0 is 1 The length of root is 4 The length of /root is 5 The length of /bin/bash is 9

  • switch语句
awk的switch语句类似于bashshell编程的case语句,可提供多个命令语句,当条件满足switch切换条件时,即切换运行指定的命令语句。其格式类似如下:
awk [options] '{switch(CONDITION){case [VALUE|/PATTERN/]:statement; case [VALUE|/PATTERN/]:statement; ...; default:statement}}'
如下例子,打印行号为6以及5的倍数的行的行号:
[root@localhost ~]# awk -F: '{switch(NR+5){case 6:{print NR; next}; case /[[:digit:]]+0/: {print NR; next} ; default: print "-------------"}}' /etc/passwd 1 ------------- ------------- ------------- 5 ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- 15 ------------- -------------

五、使用案例
  • 次数统计
    统计当前系统中以/bin/bash为默认shell的用户个数:
[root@localhost ~]# awk -F: '{ if ($NF == "/bin/bash") {shell[$NF]++}} END{for(i in shell) {print i,shell[i]}}' /etc/passwd /bin/bash 14

查看当前监听的端口统计:
[root@localhost ~]# netstat -tan | awk '/^tcp\>.*LISTEN.*/{split($4,port,"[:]"); count[port[2]]++} END{for (i in count) { printf "Port %s have %s listening link.\n",i,count[i]}}' Port 53 have 1 listening link. Port 445 have 1 listening link. Port 139 have 1 listening link. Port 631 have 1 listening link. Port 22 have 1 listening link. Port 25 have 1 listening link.

  • 查找替换
    将文件中的bash全部替换为大写的BASH后打印输出:
[root@localhost ~]# awk -F: '/.*bash$/{gsub(/bash$/,"BASH",$0); print $0}' /etc/passwd root:x:0:0:root:/root:/bin/BASH amandabackup:x:33:6:Amanda user:/var/lib/amanda:/bin/BASH postgres:x:26:26:PostgreSQL Server:/var/lib/pgsql:/bin/BASH centos:x:1001:1001::/home/centos:/bin/BASH federo:x:1002:1002::/home/federo:/bin/BASH counter:x:1003:1004::/home/counter:/bin/BASH charlie:x:1004:1005::/home/charlie:/bin/BASH Jone:x:1006:1007::/Jone:/bin/BASH Williom:x:1200:1005::/home/Williom:/bin/BASH mageia:x:1100:1100::/home/linux:/bin/BASH bash:x:2003:2003::/home/bash:/bin/BASH testbash:x:2004:2004::/home/testbash:/bin/BASH basher:x:2005:2005::/home/basher:/bin/BASH hadoop:x:2007:2007::/home/hadoop:/bin/BASH

  • 打印九九乘法表
[root@localhost ~]# awk 'BEGIN{for(n=1; n<=9; n++){for (i=1; i<=n; i++) printf i"x"n"="i*n" "; printf "\n"}}' 1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81

  • 随机数
    生成10个随机数:
[root@localhost ~]# awk 'BEGIN{srand(); for(i=1; i<=10; i++) printf "%5f\n",rand()}' | cut -d. -f2 102331 880760 844083 462001 686521 368991 974802 394346 514236 192557

统计某个网站的访问流量大小:
[root@localhost ~]# cat /var/log/httpd/access_log | awk '{sum+=$10} END{printf "%fG\n", sum/1024/1024/1024}' 0.000097G

    推荐阅读