1. 程式人生 > 實用技巧 >七,awk動作總結之一

七,awk動作總結之一

  在前文中,我們已經使用過了awk的選項、模式 以及 動作。

  這篇文章中,我們再來聊聊動作。

  不知從何說起,我們還是從之前的示例開始聊吧,回顧一個小例子,如下圖所示。

  如上圖所示,紅線標註部分就是awk命令中的"動作",我想你應該已經非常熟悉了。

  其實,我們可以把上述"動作"分解,拆開成兩部分去理解,如下圖所示。

  上圖中,我們將動作拆分成了兩個部分。

  紅線標註為第一部分:最外側的括號,即"{ }"。

  藍線標註為第二部分:"print $0"

  在之前的示例中,我們一直把上圖中的兩個部分當做一個整理去理解,但是現在,我們要把它們分開去理解。

  其實,這兩個部分都可以被稱之為"動作",只不過它們是不同"型別"的動作而已。

  "print"屬於"輸出語句"型別的動作,顧名思義,"輸出語句"型別的動作的作用就是輸出、列印資訊,沒錯,"print"與"printf"都屬於"輸出語句"型別的動作。

  "{ }"其實也可以被稱之為"動作",只不過,"{ }"屬於"組合語句"型別的動作,顧名思義,"組合語句"型別的動作的作用就是將多個程式碼組合成程式碼塊。

  這樣說可能不容易理解,我們來看個小示例,就容易理解了,示例如下。

[root@node1 ~]# cat test6
f s
1 2
1 2
[root@node1 ~]# awk '{ print $1 }{ print $2 }' test6
f
s
1
2
1
2

  如上圖所示,我們使用了兩個大括號"{ }",它們屬於"組合語句"型別的動,它們分別將兩個print括住,表示這兩個print動作分別作為兩個獨立的個體,如下圖所示。

  也就是說,我們可以這樣理解,上圖中一共有4個"動作",兩對大括號,兩個print,但是上圖中,每個大括號中只有一個動作,而我們說過,"組合語句"的作用是將多個程式碼或多個動作組合成程式碼塊,組合後的程式碼塊被當做一個整體,那麼,我們能不能把上圖中的兩個print動作組合成一個整體呢?

  必須能啊,示例如下。

[root@node1 ~]# awk '{ print $1;print $2 }' test6
f
s
1
2
1
2
[root@node1 ~]# 

  如上圖所示,我們只使用了一個大括號,將兩個print動作組合成了一個整體,但是細心如你一定發現來了,當我們把多個動作(多段程式碼)組合成一個程式碼塊的時候,每段動作(每段程式碼)之間需要用分號";"隔開,如下圖所示。

  好了,我想你應該明白了,除了print這種"輸出語句"能夠被稱之為動作以外,像"{ }"這種"組合語句"也能被稱之為動作,只不過它們的型別不同,功能也不同。

  那麼,除了"輸出語句"與"組合語句"以外,還有其他種類的動作嗎?

  必須的,我們現在就來認識另一種動作,它就是"控制語句"。

  不過,"控制語句"又有很多種,不過不用怕,我們慢慢來,一個一個聊,先來認識一種簡單的"控制語句",它就是"條件判斷"。

  如果你有過任何一種程式語言的開發經驗,你都會非常容易理解"條件判斷",條件判斷無非就是條件成立,則執行對應的程式碼,條件不成立,則不執行對應的命令,沒錯,在程式語言中,通常使用如下語法結構進行條件判斷,也就是程式設計語法中的 if 條件判斷語句。

if(條件)
{
語句1;
語句2;
...
}

  在awk中,我們同樣可以使用if這種語法進行條件判斷,只不過,上例中的語法結構是由"多行"組成,而在命令列中使用awk時,我們可以將上例中的"多行"語句寫在"一行"中,示例如下。

[root@node1 ~]# awk '{ if(NR==1){print $0} }' test6
f s

  上圖中紅線標註的部分即為"條件判斷"型別的語法,我們把紅線標註的部分單獨取出來,來描述一下。

  "if(NR == 1)"中的NR為awk的內建變數,NR為行號之意,所以,"if(NR == 1)"表示行號為1時,條件成立。

  "if(NR == 1){ print $0 }"表示行號為1是滿足條件,條件滿足時,列印整行,換句話說就是隻列印第一行。

  你可能會糾結,為什麼最外側還需要有一層大括號呢?如下圖所示。

  告訴你原因,原因就是·····

  沒有為什麼,就是要這樣寫,否則會報錯。

  如果你非要一個理由,那麼我們可以這樣理解,所有動作的最外側必須用"{ }"括起。

  你可能還是會糾結,if語句的語法結構中也包含大括號啊,那麼它屬於"組合語句"嗎?如下圖所示

  雖然它的語法結構中也包含大括號,但是我們仍然把if語句稱之為"控制語句",或者我們可以這樣理解,"控制語句"中包含"組合語句"。

  if語句中的大括號中,也可以執行多個動作,把多個程式碼段當做一個整體,也就是說,如果if所對應的條件成立,則執行if的大括號中的所有命令,示例如下。

[root@node1 ~]# awk '{ if(NR==1){print $1;print $2}}' test6
f
s

  上例表示,如果行號為1,則滿足條件,就會執行if對應的大括號中的所有程式碼,而大括號中,有兩個print動作,當條件成立時,這兩個print動作都會被執行,當條件不成立時,這兩個動作都不會執行。

  上例中,"if"對應的大括號中有多條語句,所以"if"語法中的大括號不能省略,但是,如果"if"對應的大括號中只有一條命令,那麼"if"對應的大括號則可以省略,示例如下。

  如上圖所示,當"if"對應的大括號中只有一條命令時,對應的大括號可以省略,但是需要注意,如果條件成立之後,需要執行多條語句,那麼"if"對應的大括號則不能省略。

  還記得我們在前文中使用到的"模式"嗎?示例如下

[root@node1 ~]# awk ' NR == 1 {print $0}' test6
f s

  沒錯,上圖中的用法為awk的"模式"的用法,而我們今天所介紹的用法為awk的"動作"的用法,雖然兩者在語法上有所區別,但是達到的目的相同的。

  程式語言中,除了"if"之外,還有"if...else..."或者"if...else if...else"這樣的語法,awk中也有這樣的用法。

  "if...else..."的語法如下:

if(條件)
{
語句1;
語句2;
...
}
else
{
語句1;
語句2;
...
}

  "if...else if...else"的語法如下:

if(條件1)
{
語句1;
語句2;
...
}
elseif(條件2)
{
語句1;
語句2;
...
}
else
{
語句1;
語句2;
...
}

  

  其實,這些語法與程式語言中的用法都是相同的,我相信你已經明白了,我們直接來看一些小示例吧。

  我們知道,/etc/passwd檔案中的第3列存放了使用者的ID,在centos6中,使用者ID小於500的使用者都屬於系統使用者,使用者ID大於500的使用者都屬於普通使用者。

  所以,我們可以以500為分界線,根據使用者ID判斷使用者是屬於系統使用者還是普通使用者,centos7中以1000為分界線

  我們可以通過一條awk命令,判斷出/etc/passwd檔案中的哪些使用者屬於系統使用者,哪些使用者屬於普通使用者,示例如下。

[root@node1 ~]# awk -v FS=":" '{ if($3 < 1000 ) {print $1,"系統使用者"} else { print $1,"普通使用者"}}' /etc/passwd
root 系統使用者
bin 系統使用者
daemon 系統使用者
adm 系統使用者
lp 系統使用者
sync 系統使用者
shutdown 系統使用者
halt 系統使用者
mail 系統使用者
operator 系統使用者
games 系統使用者
ftp 系統使用者
nobody 系統使用者
systemd-network 系統使用者
dbus 系統使用者
polkitd 系統使用者
sshd 系統使用者
postfix 系統使用者
chrony 系統使用者
zabbix 系統使用者
rpc 系統使用者
rpcuser 系統使用者
nfsnobody 普通使用者
ntp 系統使用者
libstoragemgmt 系統使用者
ceph 系統使用者
apache 系統使用者
jack 普通使用者
owen 普通使用者
tss 系統使用者
liuym 普通使用者
tcpdump 系統使用者
zsy1 普通使用者
zsy3 普通使用者
zsy2 普通使用者

  上圖中,就用到了"if...else..."語法,如上圖所示,$3對應了passwd檔案中的第三列,即使用者ID,如果使用者ID小於500,則輸出$1,即passwd檔案中的第一列,也就是使用者名稱,並且輸出"系統使用者"字樣,否則,則執行else中的命令,即列印使用者名稱並輸出"普通使用者"字樣,但是上例中,為了方便演示,我們並沒有對輸出的文字進行格式化,你也可以結合之前的知識,進行格式化。

[root@node1 ~]# awk -v FS=":" '{ if($3 < 1000 ) {printf "%-10s\t%s\n", $1,"系統使用者"} else { printf "%-10s\t%s\n",$1,"普通使用者"}}' /etc/passwd
root      	系統使用者
bin       	系統使用者
daemon    	系統使用者
adm       	系統使用者
lp        	系統使用者
sync      	系統使用者
shutdown  	系統使用者
halt      	系統使用者
mail      	系統使用者
operator  	系統使用者
games     	系統使用者
ftp       	系統使用者
nobody    	系統使用者
systemd-network	系統使用者
dbus      	系統使用者
polkitd   	系統使用者
sshd      	系統使用者
postfix   	系統使用者
chrony    	系統使用者
zabbix    	系統使用者
rpc       	系統使用者
rpcuser   	系統使用者
nfsnobody 	普通使用者
ntp       	系統使用者
libstoragemgmt	系統使用者
ceph      	系統使用者
apache    	系統使用者
jack      	普通使用者
owen      	普通使用者
tss       	系統使用者
liuym     	普通使用者
tcpdump   	系統使用者
zsy1      	普通使用者
zsy3      	普通使用者
zsy2      	普通使用者

  好了,再來看一個"if...else if...else"這樣的例子,其他它們都差不多,示例如下:

[root@node1 ~]# cat test7
姓名	年齡
劉月明	18
郭襄	16
老夫子  88
周瑜	36
[root@node1 ~]# awk 'NR !=1 {if($2<=30){print $1,"年輕人"} else if($2>=30 && $2<=50){print $1,"中年人"} else{print $1,"老年人"}}' test7
劉月明 年輕人
郭襄 年輕人
老夫子 老年人
周瑜 中年人

  上例中,我們使用了"關係表示式"模式,同時,在動作中,使用了"if...else if...else"這樣的"控制語句",只要前文中的知識都掌握了,那麼看懂上述示例,應該是沒有任何問題的。