Shell awk命令詳解(格式+使用方法)

2020-07-16 10:04:34
awk 命令的基本格式如下:

[[email protected] ~]# awk '條件1 {動作 1} 條件 2 {動作 2} …' 檔名


條件(Pattern)
一般使用關係表示式作為條件。這些關係表示式非常多,具體參考表1。

表 1 awk支援的主要條件型別
條件型別 條 件 說 明
awk保留字 BEGIN 在 awk 程式一開始,尚未讀取任何資料之前執行。BEGIN 後的動作只在程式開始時執行一次
awk保留字 END 在 awk 程式處理完所有資料,即將結束時執行?END 後的動作只在程式結束時執行一次
關係運算子 > 大於
< 小於
>= 大於等於
<= 小於等於
== 等於。用於判斷兩個值是否相等。如果是給變童賦值,則使用"=”
!= 不等於
A~B 判斷字串 A 中是否包含能匹配 B 表示式的子字串
A!~B 判斷字串 A 中是否不包含能匹配 B 表示式的子字串
正規表示式 /正則/ 如果在“//”中可以寫入字元,則也可以支援正規表示式

例如:
x>10:判斷變數 x 是否大於10;
x == y:判斷變數 x 是否等於變數 y;
A~B:判斷字串 A 中是否包含能匹配 B 表示式的子字串;
A!~B:判斷字串 A 中是否不包含能匹配 B 表示式的子字串;

動作(Action)
  • 格式化輸出;
  • 流程控制語句;

我們先來學習 awk 的基本用法,也就是只看看格式化輸出動作是幹什麼的。看看這個例子:

[[email protected] ~]# awk '{printf $2 "t" $6 "n"}' student.txt
#輸出第二列和第六列的內容
Name Average
Liming 87.66
Sc 85.66
Gao 91.66

在這個例子中沒有設定任何的條件型別,所以這個檔案中的所有內容都符合條件,動作會無條件執行。動作是格式化輸出 printf,"$2"和"$6"分別代表第二個欄位和第六個欄位,所以這條 awk 命令會列出 student.txt 檔案的第二個欄位和第六個欄位。

雖然都是擷取列的命令,但是 awk 命令比 cut 命令智慧多了,cut 命令是不能很好地識別空格作為分隔符的;而對於 awk 命令來說,只要分隔開,不管是空格還是製表符,都可以識別。比如剛剛擷取 df 命令的結果時,cut 命令已經力不從心了,我們來看看 awk 命令,命令如下:

[[email protected] ~]#df -h | awk '{print $1 "t" $3}'
檔案系統 已用
/dev/sda3 1.8G
tmpfs 0
/dev/sda1 26M
/dev/sr0 3.5G

在這兩個例子中,我們分別使用了 printf 動作和 print 動作。發現了嗎?如果使用 printf 動作,就必須在最後加入"n",因為 printf 只能識別標准輸出格式;如果我們不使用"n",它就不會換行。而 print 動作則會在每次輸出後自動換行,所以不用在最後加入"n"。

awk的條件

我們來看看 awk 可以支援什麼樣的條件型別吧。awk 支援的主要條件型別如表 1 所示。

1) BEGIN

BEGIN 是 awk 的保留字,是一種特殊的條件型別。BEGIN 的執行時機是"在 awk 程式一開始,尚未讀取任何資料之前"。

一旦 BEGIN 後的動作執行一次,當 awk 開始從檔案中讀入資料時,BEGIN 的條件就不再成立,所以 BEGIN 定義的動作只能被執行一次。例如:

[[email protected] ~]# awk 'BEGIN{printf "This is a transcriptn"}
{printf $2 "t" $6 "n"}' student.txt
#awk命令只要檢測不到完整的單引號就不會執行,所以這條命令的換行不用加入"",就是一行命令
#這裡定義了兩個動作
#第一個動作使用BEGIN條件,所以會在讀入檔案資料前列印"這是一張成績單"(只會執行一次)
#第二個動作會列印檔案的第二個欄位和第六個欄位
This is a transcript
Name Average
Liming 87.66
Sc 85.66
Gao 91.66

2) END

END 也是 awk 的保留字,不過剛好和 BEGIN 相反。END 是在 awk 程式處理完所有資料,即將結束時執行的。END 後的動作只在程式結束時執行一次。例如:

[[email protected] ~]# awk 'END{printf "The End n"}
{printf $2 "t" $6 "n"}' student.txt
#輸出結尾輸入"The End",這並不是文件本身的內容,而且只會執行一次
Name Average
Liming 87.66
Sc 85.66
Gao 91.66
The End

3)關係運算子

舉幾個例子看看關係運算子。假設我想看看平均成績大於等於 87 分的學員是誰,就可以這樣輸入命令:

【例 1】

[[email protected] ~]# cat student.txt | grep -v Name |awk'$6 >= 87 {printf $2'n"}'
#使用cat輸出檔案內容,用grep取反包含"Name"的行
#判斷第六個欄位(平均成績)大於等於87分的行,如果判斷式成立,則列印第六列(學員名)
Liming
Gao

在加入了條件之後,只有條件成立,動作才會執行;如果條件不滿足,則動作不執行。通過這個實驗,大家可以發現,雖然 awk 是列提取命令,但是也要按行來讀入。

這條命令的執行過程是這樣的:
  1. 如果有 BEGIN 條件,則先執行 BEGIN 定義的動作。
  2. 如果沒有 BEGIN 條件,則讀入第一行,把第一行的資料依次賦予 $0、$1、$2 等變數。其中,$0 代表此行的整體資料,$1 代表第一個欄位,$2 代表第二個欄位。
  3. 依據條件型別判斷動作是否執行。如果條件符合,則執行動作;否則讀入下一行激據。如果沒有條件,則每行都執行動作。
  4. 讀入下一行資料,重複執行社步驟。

如果我想看看 Sc 使用者的平均成績呢?

【例 2】

[[email protected] ~]# awk'$2 -/Sc/ {printf $6 "n"}' student.txt
#如果第二個欄位中包含"Sc"字元,則列印第六個欄位
85.66

這裡要注意,在 awk 中,只有使用"//"包含的字串,awk 命令才會査找。也就是說,字串必須用"//"包含,awk 命令才能正確識別。

4) 正規表示式

如果想讓 awk 識別字串,則必須使用"//"包含,例如:

[[email protected] ~]# awk '/Liming/ {print}' student.txt
#列印Liming的成績
1 Liming 82 95 86 87.66

當使用 df 命令査看分割區的使用情況時,如果我只想査看真正的系統分割區的使用情況,而不想査看光碟和臨時分割區的使用情況,則可以這樣做:

[[email protected] ~]# df -h | awk '/sda[0-9]/ {printf $1 't $5 "n"}'
#查詢包含"sda數位"的行,並列印第一個欄位和第五個欄位
/dev/sda3 10%
/dev/sda1 15%

awk流程制

之所以稱為 awk 程式設計,是因為在 awk 中允許定義變數,允許使用運算子,允許使用流程控制語句和定義函數。這樣就使得 awk 程式設計成了一門完整的程式語言,當然難度也比普通的命令要大得多。

所有語言的流程控制都非常類似,在這裡只舉一些例子,用來演示 awk 流程控制的作用。如果你現在看不懂這些例子,則可以等學習完 Bash 流程控制之後,回過頭來學習。

我們再利用 studert.txt 檔案做一個練習,後面的使用比較複雜,我們再看看這個檔案的內容,如下:

[[email protected] ~]# cat student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Gao 99 83 93 91.66

先來看看如何在 awk 中定義變數與呼叫變數的值。假設我想統計 PHP 成績的總分,就應該這樣做:

[[email protected] ~]# awk'NR==2{php1 =$3}
NR==3{php2=$3}
NR==4{php3= $3;totle=php1+php2+php3;print "totle php is" totle}' student.txt
#統計PHP成績的總分
totle php is 255

這條命令有點複雜了,我們解釋一下:
  • "NR==2{php1=$3}"(條件是NR==2,動作是php=$3) 是指如果輸入資料是第二行(第一行是標題行),就把第二行的第三個欄位的值賦予變數"php1"。
  • "NR==3{php2=$3}"是指如果輸入資料是第三行,就把第三行的第三個欄位的值賦予變數"php2"。NR==4{php3=$3;totle=php1+php2+php3;print"totle php is"totle}"("NR==4"是條件,後面{}中的都是動作)是指如果輸入數是第四行,就把第四行的第三個欄位的值賦予變數"php3";然後定義變數 totle 的值是"php1+php2+php3";最後輸出"totle php is"關鍵字,後面加變數 totle 的值。

在awk程式設計中,因為命令語句非常長,所以在輸入格式時需要注意以下內容:
  • 多個條件{動作}可以用空格分隔,也可以用回車分隔。
  • 在一個動作中,如果需要執行多條命令,則需要用分隔,或用迴車分隔。
  • 在awk中,變數的賦值與呼叫都不需要加入"$"符號。
  • 在條件中判斷兩個值是否相同,請使用"==",以便和變數賦值進行區分。

再看看如何實現流程控制。假設 Linux 成績大於 90 分,就非常棒,命令如下:

[[email protected] ~]# awk'{if (NR>=2)
{if ($4>90) printf $2" is a good man!n"}}' student.txt
#程式中有兩個if判斷,第一個判斷行號大於2,第二個判斷Linux成績大於90分
Liming is a good man!
Sc is a good man!

其實在 awk 中,if 判斷語句完全可以直接利用 awk 自帶的條件來取代,剛剛的指令碼可以改寫成這樣:

[[email protected] ~]# awk' NR>=2 {test=$4}
test>90 {printf $2" is a good man!n"}' student.txt
#先判斷行號,如果大於2,就把第四個欄位的值賦予變數test
#再判斷成績,如果test的值大於90分,就列印好男人
Liming is a good man!
Sc is a good man!