軌道
/
AWK
AWK
/
課程大綱
/
基礎概念
基礎

基礎概念 在 AWK

1 個練習

關於 基礎概念

我們會用 AWK 來指稱這門_語言_,並用 awk 或 gawk 來指稱解讀 AWK 程式的_執行檔_。

模式與動作

一個 AWK 程式由一組組模式與動作的配對組成

pattern1 { action1 }
pattern2 { action2 }
...

模式可以是對輸入進行比對的正規表示式。也可以是任何結果為 true 或 false 的運算式。

動作是一道或多道 AWK 指令。外面的大括號是必要的。

如果模式的結果為 true,就會執行對應的動作。

紀錄與欄位

AWK 會把輸入解析成一連串的紀錄。每筆紀錄會再切割成零個或多個欄位。

預設行為是:

  • 以換行字元作為「紀錄分隔符」,把輸入切成紀錄,因此每一_行_就是一筆紀錄。
  • 以空白字元作為「欄位分隔符」,把紀錄切成欄位,因此每一個_字詞_就是一個欄位。

模式與動作(續)

模式可以省略,此時動作會_對每一筆輸入紀錄_執行。

動作也可以省略,此時預設動作是印出目前的紀錄。

特殊模式

有兩個經常使用的特殊模式。

  • BEGIN會在讀取任何輸入之前執行對應的動作。
  • END會在讀完所有輸入之後執行對應的動作。

欄位變數

你可以用$1、$2、$3等來取用欄位。

目前的紀錄是$0。

Caution

說得精確一點,這些其實不算是嚴格的「變數」。$可以視為一種_運算子_,用來取得指定欄位編號的值。我們會在後續的概念中更深入探討。

以下是其他一些常用的內建變數。

控制輸入解析的內建變數

  • RS是輸入的紀錄分隔符。如前所述,它的預設值是換行字元。
  • FS是輸入的欄位分隔符。

這些變數通常會設定在BEGIN動作區塊裡。

傳達資訊的內建變數

這些變數會在程式執行期間由 AWK 自動設定。

  • NF儲存目前紀錄的欄位數量。
    • 想取得最後一個欄位的值,可以使用$NF。
  • NR儲存目前的紀錄編號。
    • 處理多個輸入檔案時,NR會保存到目前為止已處理的紀錄總數。
  • FNR儲存目前輸入檔案中目前的紀錄編號。
    • 為了說明 NR 和 FNR 的差異,這裡有個常見的 AWK 慣用寫法。
      NR == FNR {action}
      
      它的意思是「如果累計的紀錄編號等於目前檔案的紀錄編號」。這只有在_第一個輸入檔_的紀錄中才可能成立。

真假值

AWK 的真假值判定概念和 C 一樣:

  • 數字0為假。
  • 其他任何數字都為真。

由於 AWK 是文字處理語言,真假值的概念也延伸到字串。

  • 空字串""為假。
  • 任何非空字串都為真。
Note

AWK 的真假值判定概念和 C 一樣

AWK 於 1970 年代在貝爾實驗室誕生,那裡也正是 C 被發明的地方。C 對 AWK 的語法有很大的影響。

Caution

有個奇怪的地方要留意

  • 數字0為假。
  • 非空字串"0"卻為真!

我們會在後續的概念裡進一步談 AWK 的資料型態。

註解

註解以#字元開頭,一直延伸到該行結尾。

如何把輸入提供給awk

awk可以從檔案或標準輸入讀取輸入。以下這些做法都可以用。

awk '/regex/ {print $1}' file1 file2 ...

awk '/regex/ {print $1}' < file

some_command_here | awk '/regex/ {print $1}'

範例

在類 Unix 系統上,/etc/passwd存放使用者的相關資訊。每個使用者各佔一行,欄位之間以冒號分隔。

如要列出使用者名稱:

awk '
    BEGIN { FS = ":" }
    { print $1 }
' /etc/passwd

注意{print $1}這個動作沒有模式,所以每一筆紀錄都會執行它。

如果只想輸出以 bash 作為登入 shell 的使用者,可以這樣寫:

awk '
    BEGIN { FS = ":" }
    $7 == "/bin/bash" { print $1 }
' /etc/passwd

更多範例

以下是一些 awk 程式,能和常見的 Unix 指令產生相同結果。

  • 顯示檔案

    cat file.txt
    
    awk '1' file.txt
    
  • 在檔案中搜尋模式

    grep 'foo.*bar' file.txt
    
    awk '/foo.*bar/' file.txt
    
  • 只顯示檔案開頭的幾行

    head -n 20 file.txt
    
    awk '
        1
        NR == 20 {exit}
    ' file.txt
    
  • 取代檔案中的文字

    sed 's/foo/bar/' file.txt
    
    awk '{sub(/foo/, "bar")};1' file.txt
    # or more readably
    awk '
        {
            sub(/foo/, "bar")
            print
        }
    ' file.txt
    
透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 基礎概念