跳轉到內容

Awk 入門/Awk 命令列示例

來自華夏公益教科書,開放的書籍,為一個開放的世界

在命令列中使用 Awk 對文字檔案執行簡單操作非常容易。假設我有一個名為 "coins.txt" 的檔案,其中描述了我的硬幣收藏。該檔案中的每一行都包含以下資訊

  • 金屬
  • 重量(盎司)
  • 鑄造日期
  • 原產國
  • 描述

該檔案的內容如下

gold     1    1986  USA                 American Eagle
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona
silver  10    1981  USA                 ingot
gold     1    1984  Switzerland         ingot
gold     1    1979  RSA                 Krugerrand
gold     0.5  1981  RSA                 Krugerrand
gold     0.1  1986  PRC                 Panda
silver   1    1986  USA                 Liberty dollar
gold     0.25 1986  USA                 Liberty 5-dollar piece
silver   0.5  1986  USA                 Liberty 50-cent piece
silver   1    1987  USA                 Constitution dollar
gold     0.25 1987  USA                 Constitution 5-dollar piece
gold     1    1988  Canada              Maple Leaf

然後,我可以呼叫 Awk 列出所有金質硬幣,如下所示

awk '/gold/' coins.txt

這告訴 Awk 在檔案中搜索包含字串 "gold" 的文字行,並將其打印出來。結果是

gold     1    1986  USA                 American Eagle
gold     1    1908  Austria-Hungary     Franz Josef 100 Korona
gold     1    1984  Switzerland         ingot
gold     1    1979  RSA                 Krugerrand
gold     0.5  1981  RSA                 Krugerrand
gold     0.1  1986  PRC                 Panda
gold     0.25 1986  USA                 Liberty 5-dollar piece
gold     0.25 1987  USA                 Constitution 5-dollar piece
gold     1    1988  Canada              Maple Leaf

列印描述

[編輯 | 編輯原始碼]

一個評論家可能會說,這很好,但是任何 "grep" 或 "find" 工具都可以做到同樣的事情。沒錯,但是 Awk 能夠做到更多。例如,假設我只想列印描述欄位,而省略所有其他文字。那麼我可以更改對 Awk 的呼叫,如下所示

awk '/gold/ {print $5,$6,$7,$8}' coins.txt

這將產生

American Eagle
Franz Josef 100 Korona
ingot
Krugerrand 
Krugerrand  
Panda  
Liberty 5-dollar piece 
Constitution 5-dollar piece  
Maple Leaf
最簡單的 Awk 程式

此示例演示了 Awk 程式最簡單的通用形式

awk search pattern { program actions }

Awk 按行掃描輸入檔案,尋找搜尋模式。對於找到的每一行,Awk 都會執行指定的動作。在這個示例中,動作被指定為

{print $5,$6,$7,$8}

print 語句的用途很明顯。$5$6$7$8欄位或“欄位變數”,它們按數字順序儲存每行文字中的詞語。例如,$1 儲存該行中的第一個詞,$2 儲存第二個詞,依此類推。預設情況下,“詞語”或記錄被定義為任何由空格分隔的列印字元序列。

基於 "coins.txt" 的結構(見上文),欄位變數與檔案中每行文字的匹配方式如下

metal:        $1
weight:       $2
date:         $3
country:      $4
description:  $5 through $8

本示例中的程式動作列印包含描述的欄位。檔案中的描述欄位實際上可能包含一個到四個欄位,但這並不重要,因為 "print" 只是忽略任何未定義的欄位。細心的讀者會注意到 "coins.txt" 檔案井然有序,所以唯一包含多個欄位的資訊位於行的末尾。透過更改欄位分隔符(後面會解釋)可以克服這個限制。

Awk 的預設程式動作是列印整行,這正是 "print" 在沒有引數呼叫時所做的。這意味著這三個示例是相同的

awk '/gold/'
awk '/gold/ {print}'
awk '/gold/ {print $0}'

請注意,Awk 將欄位變數 $0 識別為表示整行。這有點多餘,但它確實有使其操作更明顯的優點。

條件語句

[編輯 | 編輯原始碼]

現在假設我想列出所有在 1980 年之前鑄造的硬幣。我呼叫 Awk 如下所示

awk '{if ($3 < 1980) print $3, "    ",$5,$6,$7,$8}' coins.txt

這將產生

1908    Franz Josef 100 Korona
1979    Krugerrand

這個新的示例添加了一些新的概念

列印行
  • 如果未指定搜尋模式,Awk 將匹配輸入檔案中的所有行,並在每一行上執行操作。
  • print 語句可以透過將文字包含在引號中並將其新增到引數列表中來顯示自定義文字(在本例中為四個空格)。
  • if 語句用於檢查特定條件,只有在該條件為真時才會執行 print 語句。

然而,這裡有一個微妙的問題。在大多數計算機語言中,字串是字串,數字是數字。有一些操作是針對它們中的每一種的,必須使用轉換函式將一種專門轉換為另一種。你不會連線數字,也不會對字串執行算術運算。

另一方面,Awk 並沒有在字串和數字之間進行嚴格區分。用計算機科學術語來說,它不是一種“強型別”語言。Awk 中的所有資料都被視為字串,但如果該字串恰好表示一個數字,則可以對其執行數值運算。因此,我們可以對日期欄位進行算術比較。

BEGINEND

[編輯 | 編輯原始碼]

下一個示例打印出收藏中硬幣的數量

awk 'END {print NR,"coins"}' coins.txt

這將產生

13 coins

本示例中第一個新項是 END 語句。為了解釋這一點,我必須擴充套件 Awk 程式的通用形式。

Awk 程式

每個 Awk 程式都遵循以下格式(每個部分都是可選的)

awk 'BEGIN { initializations } search pattern 1 { program actions } search pattern 2 { program actions }  ...  END { final actions }' input file

BEGIN 子句在 Awk 開始掃描輸入檔案之前執行任何所需的初始化操作。Awk 程式的後續主體由一系列搜尋模式組成,每個模式都有自己的程式動作。Awk 掃描輸入檔案中的每一行以匹配每個搜尋模式,並對找到的每個字串執行相應的動作。掃描完檔案後,可以使用 END 子句執行所需的任何最終動作。

因此,這個示例不會對輸入行本身執行任何處理。它所做的只是掃描檔案並執行一個最終動作:列印檔案中的行數,該行數由 NR 變數給出。NR 代表“記錄數”。NR 是 Awk 的“預定義”變數之一。還有其他變數,例如變數 NF 給出了該行中的欄位數,但詳細解釋要等到以後。

計算金錢

[編輯 | 編輯原始碼]

假設黃金的當前價格為每盎司 425 美元,我想計算出硬幣收藏中金質硬幣的總價值(近似值)。我呼叫 Awk 如下所示

awk '/gold/ {ounces += $2} END {print "value = $" 425*ounces}' coins.txt

這將產生

value = $2592.5

在本示例中,ounces 是我自己定義的變數,或“使用者定義”變數。在 Awk 中,幾乎任何字元序列都可以用作變數名,只要該名稱不與 Awk 中具有特定含義的某些字串衝突,例如 printNREND。無需宣告變數或初始化變數。作為字串值處理的變數將被初始化為“空字串”,這意味著如果你嘗試列印它,將沒有任何內容。作為數值處理的變數將被初始化為零。

因此,程式動作

{ounces += $2}

將每行匹配的硬幣的重量累加到變數 ounces 中。那些用 C 程式設計的人應該熟悉 += 運算子。那些不熟悉的人可以放心,這只是以下語句的簡寫形式

{ounces = ounces + $2}

最終的動作是計算並列印黃金的價值

END {print "value = $" 425*ounces}

這裡唯一有趣的是兩個列印引數——文字 value = $ 和表示式 425*ounces——用空格分隔,而不是用逗號分隔。這將兩個引數在輸出時連線在一起,沒有任何空格。

如果你沒有給 Awk 提供輸入檔案,它將允許你直接向程式輸入內容。按下 CTRL-D 將退出。
  1. 嘗試修改上面的某個程式來分別計算並顯示金銀的總量(以盎司為單位),但使用同一個程式。你將不得不使用兩對模式/動作。
  2. 編寫一個 Awk 程式,查詢在美國鑄造的所有硬幣的平均重量。
  3. 編寫一個 Awk 程式,在每行文字之前重新列印其輸入內容,並在其前面加上行號。

在下一章中,我們將學習如何編寫長度超過一行的 Awk 程式。

華夏公益教科書