上一篇介紹了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]#