(轉)關於 awk 的 pattern(模式)

snale1989發表於2016-09-22

本文轉自chinaunix http://bbs.chinaunix.net/thread-4246512-1-1.html   作者reyleon

我們知道, awk程式由一系列 pattern 以及與之對應的 action 組成的 rule 組成,rule之間用";"分號隔開, 一條輸入記錄與 pattern 匹配則執行與之關聯的action, 如下所示:

awk ' 
        pattern { action };
        pattern { action };
        .....
'

可是, 很多人並不清楚什麼東西可以做為 pattern下面就來聊聊這些個事兒. 以下文字做為測試文字:

 1 $ cat myfile
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

 

首先, 要記住, 凡是被 {} 包裹的, 就是 action, 或者說, action 必然被 {} 包裹著
凡是沒有被{}包裹的, 就是pattern, 或者說pattern不能被{}包裹著. 

一. 正規表示式做為 pattern

最常見的, 就是一個正規表示式做為一個 pattern了, 如:

1 awk '/555-5553/ { print $0 }' myfile

/555-5553/ 就是一個正規表示式, 如果輸入記錄匹配555-5553, 就輸出這條記錄, 這裡只有第一行匹配 555-5553, 所以就輸出了第一行這條記錄.

1 $ awk '/555-5553/ { print $0 }' myfile
2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F

二. 比較表示式做為 pattern

1 $ awk '$NF == "A" { print $0 }' myfile
2 Anthony    555-3412  anthony.asserturo@hotmail.com    A
3 Becky      555-7685  becky.algebrarum@gmail.com       A
4 Bill       555-1675  bill.drowning@hotmail.com        A
5 Martin     555-6480  martin.codicibus@hotmail.com     A

最後一個欄位為 "A" 的, 輸出這條記錄.

1 $ awk '$NF != "A" { print $0 }' myfile
2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
3 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
4 Camilla    555-2912  camilla.infusarum@skynet.be      R
5 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
6 Julie      555-6699  julie.perscrutabor@skeeve.com    F
7 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

最後一個欄位不為 "A" 的, 輸出這條記錄.

三. 常量表示式做為 pattern

這種pattern一般是最不易被新手理解的, 事實上, 既然是 pattern 匹配, 結果就只有兩種情況, 要麼匹配(即為真),就執行後面的 action, 要麼不匹配(即為假), 就不會執行後面的action.
所以, awk 的規則基本上也就是:

awk ' 真 { 執行程式碼 }; 假 { 不執行程式碼 }' 

也理解為:

awk ' 條件 { 動作 } 條件 { 動作 } ' 

什麼東西可以做為一個常量? 一個數字, 或者一個字串, 都可以做為一個常量 pattern. 那這裡就有一個龜腚了:

凡是非0的數字, 就表示pattern匹配成功, 也就是pattern為真. 否則表示匹配失敗, 為假.
凡是非空的字串, 就表示pattern匹配成功, 也就是pattern為真. 否則表示匹配失敗, 為假. 

注意: 字串是由引號引起來的! 比如數字 0 與 字串 "0" 不是一樣的. 數字0為假, 字串"0"為真(不為空).

如:

 1 $ awk '1 { print $0 }' myfile
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R
 1 $ awk '2 { print $0 }' myfile
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R
1 $ awk '0 { print $0 }' myfile
2 // 數字 0 做為 pattern, 0為假, pattern 匹配失敗, 所以不執行 print $0, 沒有列印.

 

 1 $ awk ' "0" { print $0 }' myfile
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R
13 // 字串 "0" 做為 pattern, 為真, pattern 匹配成功, 所以執行 print $0, 列印.

還有一些, 可能是寫錯的賦值語句做為了pattern的, 或者其他亂七八糟的, 等等..:

1 $ awk 'a=0'  myfile // 數字0. 假, 沒輸出.

 

 1 $ awk 'a=1'  myfile // 1 為真. 
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R
 1 $ awk 'x-1'  myfile // x 為未定義的變數, 做數學運算, 結果為 -1 , 因 -1 也是非0的數字常量, pattern 匹配成功, 為真, print $0.
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

 

1 1 $ awk 'xxoo'  myfile //xxoo為變數, 未賦值, 為假
 1 $ awk '"xxoo"'  myfile // 字串, 真.
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

四. 空 pattern

 1 $ awk '{print $0}' myfile
 2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
 3 Anthony    555-3412  anthony.asserturo@hotmail.com    A
 4 Becky      555-7685  becky.algebrarum@gmail.com       A
 5 Bill       555-1675  bill.drowning@hotmail.com        A
 6 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
 7 Camilla    555-2912  camilla.infusarum@skynet.be      R
 8 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
 9 Julie      555-6699  julie.perscrutabor@skeeve.com    F
10 Martin     555-6480  martin.codicibus@hotmail.com     A
11 Samuel     555-3430  samuel.lanceolis@shu.edu         A
12 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

// 這裡沒有 pattern, 空的, empty pattern, 那麼就表示匹配輸入記錄永遠成功, 也就是說永遠為真, 這是龜腚. action永遠都會執行.

我們知道, awk 的rule 就是由一系列 pattern 和 action 構成, 這裡所謂的空 pattern, 也可以說是省略了 pattern, 那麼 action 可不可以省略呢?
事實上, action也是可以省略的, 但如果省略了的話,它就會有一個預設的action行為, 即 { print $0 } !
很多人新手經常搞不懂一下的程式碼:

awk '{a=1}1' myfile

經常會碰到有人問, 這個程式碼後面的1是幹嘛的? 其實, 我們拆分下 awk 的 rule 就明白了.
awk '{a=1}1' 這個語句包含了兩條規則:

awk '
        [空pattern] {a=1}    #第一條規則
        1 [{省略的action}]   #第二條規則
'


第一條rule: {a=1} , 這條規則這裡有一個{}大括號包裹著, 表示這是一個 action, 但是省略了pattern, 即空pattern, 
上面說了, 空pattern是永遠匹配為真的, 
所以{a=1}這個action會針對每條輸入記錄對執行,只是我們看不到它的具體表現而已.

第二條rule: 1, 這條規則僅僅只有一個1字, 沒有被{}大括號包裹著, 
所以這個1是一個pattern, 省略了{action}, 而這個數字 1 , 實際上就是一個常量表示式pattern, 因它為非0的數字,所以pattern匹配成功,為真,就執行action,
因為這裡省略了{action},就觸發預設的行為,而預設的action行為是print $0,即列印這條記錄.

再如: awk '1;1' ,省略了兩個action, 所以這條實際上就是兩個 {print $0}{print $0}.

所以, 凡是action只是要輸出這條記錄的, 通通都可以省略這個 action.

如:

1 $ awk '/Amelia/ || /Martin/'  myfile
2 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
3 Martin     555-6480  martin.codicibus@hotmail.com     A
4 $ awk '$NF ~ /F/'  myfile
5 Amelia     555-5553  amelia.zodiacusque@gmail.com     F
6 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
7 Julie      555-6699  julie.perscrutabor@skeeve.com    F
8 $ awk 'NR==5'  myfile
9 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R

五. 特殊的 pattern: BEGIN, END ..

在 awk 中, 肯定會經常看到 BEGIN, END 這兩個玩意兒. 如 awk 'BEGIN { ... } END { ... } '

實際上, BEGIN 和 END 只是兩個特殊的 pattern . 類似的還有 BEGINFILE,  ENDFILE. 

BEGIN 在讀入檔案之前匹配成功, 即為在讀入檔案之前這條 rule 就已經執行了.
END 在處理完檔案之後才匹配陳宮, 即在處理完檔案之後才會執行這條 rule.

1 $ awk 'BEGIN { n=5 } NR==n { print $0 } END { print $0 }' myfile
2 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
3 Jean-Paul  555-2127  jeanpaul.campanorum@nyu.edu      R

分析下這條 awk 語句:
首先, 它包含三條規則
1. BEGIN { n=5 }  ,BEGIN 為 特殊pattern, {n=5} 為 action
2. NR==n { print $0 }, NR==n 是一個比較表示式pattern, { print $0 } 為action.
3. END { print $0 }, END 也是一個特殊的pattern.

BEGIN 模式一般讀入檔案之前常用的是做一些相關的定義操作, 這裡設定變數n=5. 
然後awk開始處理記錄, 當 NR==n 時, 即處理到我們定義的那條記錄時(第5條記錄時), 執行 print $0, 輸出這條記錄.
最後是END模式, awk處理完檔案裡, END模式匹配成功,執行print $0, 即輸出最後一條記錄.
所以, 結果是輸出第5條記錄和最後一條記錄.


六. 模式範圍: begpat, endpat

這個模式範圍, 是由兩個 pattern 組成, 每個 pattern可以是任意的非特殊型別(非BEGIN/END模式型別)的pattern型別(可以是正則,比較,常量等pattern).
這個模式範圍匹配的規則有點兒特殊, 這裡以一個"開閘放水"的例子做為一個類比.
1. 首先以第一個pattern匹配輸入記錄, 如果第一個pattern匹配成功,就"開啟放水的開關開始放水",[開關的狀態: 開], 即會立即執行後面的action.此時不管第二個pattern是否匹配.
2. 接著再匹配第二pattern, 如果第二pattern匹配失敗,開關的狀態不變,即還是會執行action.
3. 接著繼續以第二個pattern匹配下一條輸入記錄,直到第二個pattern匹配成功. 就"關閉放水的開關停止放水",[開關的狀態: 關], 模式範圍匹配結束.
4. 以1,2,3步驟進入下一輪模式範圍匹配

如:

1 $ awk 'NR==4, /555-3430/ { print $0}' myfile
2 Bill       555-1675  bill.drowning@hotmail.com        A
3 Broderick  555-0542  broderick.aliquotiens@yahoo.com  R
4 Camilla    555-2912  camilla.infusarum@skynet.be      R
5 Fabius     555-1234  fabius.undevicesimus@ucb.edu     F
6 Julie      555-6699  julie.perscrutabor@skeeve.com    F
7 Martin     555-6480  martin.codicibus@hotmail.com     A
8 Samuel     555-3430  samuel.lanceolis@shu.edu         A

先執行 NR==4 這個 pattern 匹配, 當第四條記錄匹配成功時, 放水開關開啟, 開始放水了, 即開始執行action , 執行 print $0 , 輸出第四條記錄.
接著使用 /555-3430/ 當前記錄, 不成功.
第5條記錄繼續執行 action.繼續以第二個pattern匹配這條記錄..不成功..,開關是開的
第6條記錄繼續執行 action.繼續以第二個pattern匹配這條記錄..不成功..,開關是開的
...
..
直到匹配 /555-3430/, 第二個pattern匹配成功. 開關關閉. 模式範圍匹配結束..
開始下一輪模式範圍匹配..


以上就是各種 pattern 的簡要解說了.. 至於 action, 也沒啥可說的, action一般做具體的事情.
那些個流程控制語句 if/for/while 等一般都屬於action了, 不能做為 pattern了.

相關文章