(一)AWK基礎入門
前言
awk是一個報告生成器,它擁有強大的文字格式化的能力,這就是專業的說法。
awk是由Alfred Aho 、Peter Weinberger 和 Brian Kernighan這三個人創造的,awk由這個三個人的姓氏的首個字母組成。
awk早期是在unix上實現的,所以,我們現在在linux的所使用的awk其實是gawk,也就是GNU awk,簡稱為gawk,awk還有一個版本,New awk,簡稱為nawk,但是linux中最常用的還是gawk。
awk其實是一門程式語言,它支援條件判斷、陣列、迴圈等功能。所以,我們也可以把awk理解成一個指令碼語言直譯器。
grep 、sed、awk被稱為linux中的"三劍客"。我們總結一下這三個"劍客"的特長。
- grep 更適合單純的查詢或匹配文字
- sed 更適合編輯匹配到的文字
- awk 更適合格式化文字,對文字進行較複雜格式處理
AWK基礎
awk基本語法如下,看不懂沒關係,我們會慢慢舉例。
awk [options] 'program' file1 , file2 , ```
對於上述語法中的program來說,又可以細分成pattern和action,也就是說,awk的基本語法如下
awk [options] 'Pattern{Action}' file
從字面上理解 ,action指的就是動作,awk擅長文字格式化,並且將格式化以後的文字輸出,所以awk最常用的動作就是print和printf,因為awk要把格式化完成後的文字輸出啊,所以,這兩個動作最常用。
我們先從最簡單用法開始瞭解awk,我們先不使用[options] ,也不指定pattern,直接使用最簡單的action,從而開始認識awk,示例如下
[root@node1 ~]# awk '{print}' testd
ddd
[root@node1 ~]# echo ddd>testd
[root@node1 ~]# awk '{print}' testd
ddd
上圖中,我們只是使用awk執行了一個列印的動作,將testd檔案中的內容列印了出來。
好了,現在,我們來操作一下另一個類似的場景。
[root@node1 ~]# df 檔案系統 1K-塊 已用 可用 已用% 掛載點 devtmpfs 2960724 0 2960724 0% /dev tmpfs 2972456 16 2972440 1% /dev/shm tmpfs 2972456 46256 2926200 2% /run tmpfs 2972456 0 2972456 0% /sys/fs/cgroup /dev/mapper/centos-root 62670952 3664876 59006076 6% / /dev/sda1 201380 167100 34280 83% /boot /dev/mapper/centos-home 20961280 33128 20928152 1% /home /dev/mapper/centos-opt 20961280 33056 20928224 1% /opt tmpfs 2972456 24 2972432 1% /var/lib/ceph/osd/ceph-2 tmpfs 594492 0 594492 0% /run/user/0 [root@node1 ~]# df|awk '{print $5}' 已用% 0% 1% 2% 0% 6% 83% 1% 1% 1% 0%
上圖中的示例沒有使用到options和pattern,上圖中的awk '{print $5}',表示輸出df的資訊的第5列,$5表示將當前行按照分隔符分割後的第5列,不指定分隔符時,預設使用空格作為分隔符,細心的你一定發現了,上述資訊用的空格不止有一個,而是有連續多個空格,awk自動將連續的空格理解為一個分割符了,是不是比cut命令要簡單很多,這樣比較簡單的例子,有利於我們開始瞭解awk。
awk是逐行處理的,逐行處理的意思就是說,當awk處理一個文字時,會一行一行進行處理,處理完當前行,再處理下一行,awk預設以"換行符"為標記,識別每一行,也就是說,awk跟我們人類一樣,每次遇到"回車換行",就認為是當前行的結束,新的一行的開始,awk會按照使用者指定的分割符去分割當前行,如果沒有指定分割符,預設使用空格作為分隔符。
$0 表示顯示整行 ,$NF表示當前行分割後的最後一列($0和$NF均為內建變數)
注意,$NF 和 NF 要表達的意思是不一樣的,對於awk來說,$NF表示最後一個欄位,NF表示當前行被分隔符切開以後,一共有幾個欄位。
也就是說,假如一行文字被空格分成了7段,那麼NF的值就是7,$NF的值就是$7, 而$7表示當前行的第7個欄位,也就是最後一列,那麼每行的倒數第二列可以寫為$(NF-1)。
我們也可以一次輸出多列,使用逗號隔開要輸出的多個列,如下,一次性輸出第一列和第二列
[root@node1 ~]# awk '{print $1,$2}' test
abc 123
8ua 456
[root@node1 ~]# cat test
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@node1 ~]# awk '{print $1,$2}' test
abc 123
8ua 456
同理,也可以一次性輸出多個指定的列,如下圖
[root@node1 ~]# awk '{print $1,$2,$5}' test
abc 123
8ua 456 7y7
我們發現,第一行並沒有第5列,所以並沒有輸出任何文字,而第二行有第五列,所以輸出了。
除了輸出文字中的列,我們還能夠新增自己的欄位,將自己的欄位與檔案中的列結合起來,如下做法,都是可以的。
[root@node1 ~]# awk '{print "First:"$1,"Second:"$2}' test
First:abc Second:123
First:8ua Second:456
從上述實驗中可以看出,awk可以靈活的將我們指定的字元與每一列進行拼接,或者把指定的字元當做一個新列插入到原來的列中,也就是awk格式化文字能力的體現。
但是要注意,$1這種內建變數的外側不能加入雙引號,否則$1會被當做文字輸出,示例如下
[root@node1 ~]# awk '{print $1}' test
abc
8ua
[root@node1 ~]# awk '{print $1,"$1"}' test
abc $1
8ua $1
[root@node1 ~]# awk '{print $1,$1}' test
abc abc
8ua 8ua
我們也可以輸出整行,比如,如下兩種寫法都表示輸出整行。
[root@node1 ~]# awk '{print}' test
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@node1 ~]# awk '{print $0}' test
abc 123 iuy ddd
8ua 456 auv ppp 7y7
我們說過,awk的語法如下
awk [options] 'Pattern{Action}' file
而且我們說過awk是逐行處理的, 剛才已經說過了最常用的Action:print
現在,我們來認識下一Pattern,也就是我們所說的模式
不過,我們準備先把awk中最特殊的模式展示給大家,以後再介紹普通的模式,因為普通模式需要的篇幅比較長,所以我們先來總結特殊模式。
AWK 包含兩種特殊的模式:BEGIN 和 END。
BEGIN 模式指定了處理文字之前需要執行的操作:
END 模式指定了處理完所有行之後所需要執行的操作:
什麼意思呢?光說不練不容易理解,我們來看一些小例子,先從BEGIN模式開始,示例如下
[root@node1 ~]# cat test
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@node1 ~]# awk 'BEGIN{print "aaa","bbb"}' test
aaa bbb
上述寫法表示,在開始處理test檔案中的文字之前,先執行列印動作,輸出的內容為"aaa","bbb".
也就是說,上述示例中,雖然指定了test檔案作為輸入源,但是在開始處理test文字之前,需要先執行BEGIN模式指定的"列印"操作
既然還沒有開始逐行處理test檔案中的文字,那麼是不是根本就不需要指定test檔案呢,我們來試試。
[root@node1 ~]# awk 'BEGIN{print "aaa","bbb"}'
aaa bbb
經過實驗發現,還真是,我們並沒有給定任何輸入來源,awk就直接輸出資訊了,因為,BEGIN模式表示,在處理指定的文字之前,需要先執行BEGIN模式中指定的動作,而上述示例沒有給定任何輸入源,但是awk還是會先執行BEGIN模式指定的"列印"動作,列印完成後,發現並沒有文字可以處理,於是就只完成了"列印 aaa bbb"的操作。
[root@node1 ~]# cat test
abc 123 iuy ddd
8ua 456 auv ppp 7y7
[root@node1 ~]# awk 'BEGIN{print "aaa","bbb"} {print $1,$2}' test
aaa bbb
abc 123
8ua 456
上圖中,藍色標註的部分表示BEGIN模式指定的動作,這部分動作需要在處理指定的文字之前執行,所以,上圖中先打印出了"aaa bbb",當BEGIN模式對應的動作完成後,在使用後面的動作處理對應的文字,即列印test檔案中的第一列與第二列,這樣解釋應該比較清楚了吧。
看完上述示例,似乎更加容易理解BEGIN模式是什麼意思了,BEGIN模式的作用就是,在開始逐行處理文字之前,先執行BEGIN模式所指定的動作。以此類推,END模式的作用就一目瞭然了,舉例如下。
[root@node1 ~]# awk '{print $1,$2} END{print "ccc","ddd"}' test
abc 123
8ua 456
ccc ddd
聰明如你一定明白了,END模式就是在處理完所有的指定的文字之後,需要指定的動作。
那麼,我們可以結合BEGIN模式和END模式一起使用。示例如下
[root@node1 ~]# awk 'BEGIN{print "aaa","bbb"} {print $1,$2} END{print "ccc","ddd"}' test
aaa bbb
abc 123
8ua 456
ccc ddd
上述示例中返回的結果有沒有很像一張"報表",有"表頭" 、"表內容"、 "表尾",awk對文字的格式化能力你體會到了嗎?