Awk 入門/Awk 程式檔案
有時 Awk 程式需要反覆使用。在這種情況下,從 shell 指令碼執行 Awk 程式非常簡單。例如,考慮一個將檔案中的每個單詞列印在單獨一行的 Awk 指令碼。這可以使用名為“words”的指令碼完成,其中包含
awk '{c=split($0, s); for(n=1; n<=c; ++n) print s[n] }' $1
然後可以使“Words”可執行(使用“chmod +x words”),並像任何其他命令一樣呼叫生成的 shell“程式”。例如,
“words”可以從“vi”文字編輯器中呼叫,如下所示
:%!words
這會將所有文字變成一個單字列表。
- 再舉一個例子,考慮前面提到的雙倍行距程式。這可以稍微更改以接受標準輸入,使用前面描述的“-”,然後複製到名為“double”的檔案中
awk '{print; if (NF != 0) print ""}' -
—然後可以從“vi”呼叫它來對編輯器中的所有文字進行雙倍行距。
- 下一步將是允許“double”執行相反的操作:將雙倍行距檔案恢復為單倍行距,使用選項
undouble
當然,任務的第一部分是設計一種方法來刪除多餘的空行,而不透過刪除所有空行來破壞原始單倍行距檔案的間距。最簡單的方法是刪除連續空行塊中的每隔一行。這並不一定能保留原始間距,但它會以某種形式保留間距。
實現此方法也很簡單,涉及使用名為“skip”的變數。每當跳過空行時,此變數被設定為“1”,以告訴 Awk 程式不要跳過下一行。方案如下
BEGIN {set skip to 0}
scan the input:
if skip == 0 if line is blank
skip = 1
else
print the line
get next line of input
if skip == 1 print the line
skip = 0
get next line of input
這直接轉換為以下 Awk 程式
BEGIN {skip = 0}
skip == 0 {if (NF == 0)
{skip = 1}
else
{print};
next}
skip == 1 {print;
skip = 0;
next}
該程式可以放在一個單獨的檔案中,命名為“undouble.awk”,並編寫 shell 指令碼“undouble”為
awk -f undouble.awk
它也可以直接嵌入到 shell 指令碼中,使用單引號將程式括起來,並使用反斜槓(“\”)允許多行
awk 'BEGIN {skip = 0} \
skip == 0 {if (NF == 0)
{skip = 1} \
else
{print}; \
next} \
skip == 1 {print; \
skip = 0; \
next}'
請記住,當使用“\”將 Awk 程式嵌入到指令碼檔案中時,該程式在 Awk 中顯示為一行。必須使用分號來分隔命令。
再舉一個更復雜的例子,我遇到一個問題,當我編寫文字文件時,有時我會意外地輸入兩次同一個詞:“結果也是也是那樣......”。這些重複的詞在校對時很難發現,但編寫一個 Awk 程式來完成這項工作很簡單,它掃描文字檔案以查詢重複項;如果找到重複項,則列印重複的詞和它所在的行;否則列印“未找到重複項”。
BEGIN { dups=0; w="xy-zzy" }
{ for( n=1; n<=NF; n++)
{ if ( w == $n ) { print w, "::", $0 ; dups = 1 } ; w = $n }
}
END { if (dups == 0) print "No duplicates found." }
“w”變數儲存檔案中每個詞,將其與檔案中的下一個詞進行比較;w 被初始化為“xy-zzy”,因為這不太可能是檔案中的一個詞。變數“dup”被初始化為 0,如果找到重複項則設定為 1;如果在結尾時仍然為 0,則程式列印“未找到重複項”訊息。與前面的示例一樣,我們可以將其放入一個單獨的檔案中,或者將其嵌入到指令碼檔案中。
- 最後這些示例使用變數來允許 Awk 程式跟蹤它一直在做什麼。如前所述,Awk 以迴圈方式執行:獲取一行,處理它,獲取下一行,處理它,等等;為了使 Awk 程式記住迴圈之間的內容,它需要在變數中留下一個便條。
例如,假設我們要匹配第一欄位值為 1,000 的行,但隨後列印下一行。我們可以這樣做:
BEGIN {flag = 0}
$1 == 1000 {flag = 1;
next}
flag == 1 {print;
flag = 0;
next}
該程式在找到以 1,000 開頭的行時設定一個名為“flag”的變數,然後獲取下一行輸入。列印下一行輸入,然後清除“flag”,以便下一行不會被列印。
如果我們想列印接下來的五行,我們可以使用一個名為“counter”的變數以幾乎相同的方式做到這一點
BEGIN {counter = 0}
$1 == 1000 {counter = 5;
next}
counter > 0 {print;
counter--;
next}
該程式在找到以 1,000 開頭的行時將名為“counter”的變數初始化為 5;對於接下來的 5 行輸入,它列印它們並將“counter”遞減,直到它為零。
這種方法可以根據需要進行擴充套件。假設我們有一個輸入五行的五種不同操作的列表,要在匹配一行輸入後執行;然後我們可以建立一個名為“state”的變數,它儲存接下來要執行的列表中的哪個專案。該方案通常如下
BEGIN {set state to 0}
scan the input:
if match set state to 1
get next line of input
if state == 1 do the first thing in the list
state = 2
get next line of input
if state == 2 do the second thing in the list
state = 3
get next line of input
if state == 3 do the third thing in the list
state = 4
get next line of input
if state == 4 do the fourth thing in the list
state = 5
get next line of input
if state == 5 do the fifth (and last) thing in the list
state = 0
get next line of input
這被稱為“狀態機”。在這種情況下,它正在執行一個簡單的操作列表,但相同的方法也可以用於執行更復雜的動作分支序列,就像我們在流程圖中而不是簡單的列表中可能遇到的那樣。
我們可以將狀態號分配給流程圖中的塊,然後使用 if-then 測試來設定狀態變數,以指示接下來應該執行哪個備用操作。但是,很少有 Awk 程式需要如此複雜,在這裡介紹更詳細的示例可能會比它值得的更令人困惑。需要記住的重要一點是,awk 程式可以在一行掃描迴圈中在變數中給自己留言,以告訴它在以後的行掃描迴圈中該做什麼。