一、簡介
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-autoipd UID:170
User:systemd-bus-proxy UID:999
User:systemd-network UID:998
User:polkitd UID:997
User:charlie UID: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