七,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"這樣的"控制語句",只要前文中的知識都掌握了,那麼看懂上述示例,應該是沒有任何問題的。