1. 程式人生 > 其它 >(一)AWK基礎入門

(一)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對文字的格式化能力你體會到了嗎?