Linux中國

awk 中的欄位、記錄和變數

Awk 有好幾個變種:最早的 awk,是 1977 年 AT&T 貝爾實驗室所創。它還有一些重構版本,例如 mawknawk。在大多數 Linux 發行版中能見到的,是 GNU awk,也叫 gawk。在大多數 Linux 發行版中,awkgawk 都是指向 GNU awk 的軟鏈接。輸入 awk,調用的是同一個命令。GNU awk 用戶手冊中,能看到 awkgawk 的全部歷史。

這一系列的第一篇文章 介紹了 awk 命令的基本格式:

$ awk [選項] '模式 {動作}' 輸入文件

awk 是一個命令,後面要接選項 (比如用 -F 來定義欄位分隔符)。想讓 awk 執行的部分需要寫在兩個單引號之間,至少在終端中需要這麼做。在 awk 命令中,為了進一步強調你想要執行的部分,可以用 -e 選項來突出顯示(但這不是必須的):

$ awk -F, -e '{print $2;}' colours.txt
yellow
blue
green
[...]

記錄和欄位

awk 將輸入數據視為一系列記錄,通常是按行分割的。換句話說,awk 將文本中的每一行視作一個記錄。每一記錄包含多個欄位。一個欄位由欄位分隔符分隔開來,欄位是記錄的一部分。

默認情況下,awk 將各種空白符,如空格、製表符、換行符等視為分隔符。值得注意的是,在 awk 中,多個空格將被視為一個分隔符。所以下面這行文本有兩個欄位:

raspberry red

這行也是:

tuxedo                  black

其他分隔符,在程序中不是這麼處理的。假設欄位分隔符是逗號,如下所示的記錄,就有三個欄位。其中一個欄位可能會是 0 個位元組(假設這一欄位中不包含隱藏字元)

a,,b

awk 程序

awk 命令的程序部分是由一系列規則組成的。通常來說,程序中每個規則佔一行(儘管這不是必須的)。每個規則由一個模式,或一個或多個動作組成:

模式 { 動作 }

在一個規則中,你可以通過定義模式,來確定動作是否會在記錄中執行。模式可以是簡單的比較條件、正則表達式,甚至兩者結合等等。

這個例子中,程序只會顯示包含單詞 「raspberry」 的記錄:

$ awk '/raspberry/ { print $0 }' colours.txt
raspberry red 99

如果沒有文本符合模式,該動作將會應用到所有記錄上。

並且,在一條規則只包含模式時,相當於對整個記錄執行 { print },全部列印出來。

Awk 程序本質上是數據驅動的,命令執行結果取決於數據。所以,與其他編程語言中的程序相比,它還是有些區別的。

NF 變數

每個欄位都有指定變數,但針對欄位和記錄,也存在一些特殊變數。NF 變數,能存儲 awk 在當前記錄中找到的欄位數量。其內容可在屏幕上顯示,也可用於測試。下面例子中的數據,來自上篇文章文本

$ awk '{ print $0 " (" NF ")" }' colours.txt
name       color  amount (3)
apple      red    4 (3)
banana     yellow 6 (3)
[...]

awkprint 函數會接受一系列參數(可以是變數或者字元串),並將它們拼接起來。這就是為什麼在這個例子里,每行結尾處,awk 會以一個被括弧括起來的整數表示欄位數量。

NR 變數

另外,除了統計每個記錄中的欄位數,awk 也統計輸入記錄數。記錄數被存儲在變數 NR 中,它的使用方法和其他變數沒有任何區別。例如,為了在每一行開頭顯示行號:

$ awk '{ print NR ": " $0 }' colours.txt
1: name       color  amount
2: apple      red    4
3: banana     yellow 6
4: raspberry  red    3
5: grape      purple 10
[...]

注意,寫這個命令時可以不在 print 後的多個參數間添加空格,儘管這樣會降低可讀性:

$ awk '{print NR": "$0}' colours.txt

printf() 函數

為了讓輸出結果時格式更靈活,你可以使用 awkprintf() 函數。 它與 C、Lua、Bash 和其他語言中的 printf 相類似。它也接受以逗號分隔的格式參數。參數列表需要寫在括弧里。

$ printf 格式, 項目1, 項目2, ...

格式這一參數(也叫格式符)定義了其他參數如何顯示。這一功能是用格式修飾符實現的。%s 輸出字元,%d 輸出十進位數字。下面的 printf 語句,會在括弧內顯示欄位數量:

$ awk 'printf "%s (%d)n",$0,NF}' colours.txt
name       color  amount (3)
raspberry  red    4 (3)
banana     yellow 6 (3)
[...]

在這個例子里,%s (%d) 確定了每一行的輸出格式,$0,NF 定義了插入 %s%d 位置的數據。注意,和 print 函數不同,在沒有明確指令時,輸出不會轉到下一行。出現轉義字元 n 時才會換行。

Awk 腳本編程

這篇文章中出現的所有 awk 代碼,都在 Bash 終端中執行過。面對更複雜的程序,將命令放在文件(腳本)中會更容易。-f FILE 選項(不要和 -F 弄混了,那個選項用於欄位分隔符),可用於指明包含可執行程序的文件。

舉個例子,下面是一個簡單的 awk 腳本。創建一個名為 example1.awk 的文件,包含以下內容:

/^a/ {print "A: " $0}
/^b/ {print "B: " $0}

如果一個文件包含 awk 程序,那麼在給文件命名時,最好寫上 .awk 的擴展名。 這樣命名不是強制的,但這麼做,會給文件管理器、編輯器(和你)一個關於文件內容的很有用的提示。

執行這一腳本:

$ awk -f example1.awk colours.txt
A: raspberry  red    4
B: banana     yellow 6
A: apple      green  8

一個包含 awk 命令的文件,在最開頭一行加上釋伴 #!,就能變成可執行腳本。創建一個名為 example2.awk 的文件,包含以下內容:

#!/usr/bin/awk -f
#
# 除了第一行,在其他行前顯示行號
#

NR > 1 {
    printf "%d: %sn",NR,$0
}

可以說,腳本中只有一行,大多數情況下沒什麼用。但在某些情況下,執行一個腳本,比記住,然後打一條命令要容易的多。一個腳本文件,也提供了一個記錄命令具體作用的好機會。以 # 號開頭的行是注釋,awk 會忽略它們。

給文件可執行許可權:

$ chmod u+x example2.awk

執行腳本:

$ ./example2.awk colours.txt
2: apple      red    4
2: banana     yellow 6
4: raspberry red    3
5: grape      purple 10
[...]

awk 命令放在腳本文件中,有一個好處就是,修改和格式化輸出會更容易。在終端中,如果能用一行執行多條 awk 命令,那麼輸入多行,才能達到同樣效果,就顯得有些多餘了。

試一試

你現在已經足夠了解,awk 是如何執行指令的了。現在你應該能編寫複雜的 awk 程序了。試著編寫一個 awk 腳本,它需要: 至少包括一個條件模式,以及多個規則。如果你想使用除 printprintf 以外的函數,可以參考在線 gawk 手冊

下面這個例子是個很好的切入點:

#!/usr/bin/awk -f
#
#  顯示所有記錄 除了出現以下情況
#  如果第一個記錄 包含 「raspberry」
#  將 「red」 替換成 「pi」

$1 == "raspberry" {
        gsub(/red/,"pi")
}

{ print }

試著執行這個腳本,看看輸出是什麼。接下來就看你自己的了。

這一系列的下一篇文章,將會介紹更多,能在更複雜(更有用!) 腳本中使用的函數。

這篇文章改編自 Hacker Public Radio 系列,一個技術社區博客。

via: https://opensource.com/article/19/11/fields-records-variables-awk

作者:Seth Kenlon 選題:lujun9972 譯者:wenwensnow 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國