2 概述
AWK于1970年代誕生于貝爾實驗室, 它是一門被設計來處理基于文本的數據—包括文件或者數據流—的通用編程語言.我注意到Erik Wendelin寫過一篇文章. 在這篇文章中他說到, 介紹awk的最好方式是實例. 對此我非常同意.
Eric Pement的AWK單行程序合集包含五小節:
1.文件間距
2.計數和計算
3.文本轉換和替換
4.特定行的輸出
5.特定行的刪除
本文的第一部分解釋前兩個小節:"文件間距"和"計數與計算". 第二部分介紹"文本轉換和替換", 最后一部分解釋"特定行的輸出/刪除".這些簡短的例子能夠工作于所有版本的awk, 包括nawk(AT&T's new awk), gawk(GNU's awk), mawk(Michael Brennan's awk), 以及oawk(old awk).
讓我們開始吧!
3 行間距
3.1 行間距加倍
awk '1; { print "" }' filname.ext
它是如何工作的呢? 這一行就是一個awk程序, 每個awk程序都包含一系列的pattern-action語句"pattern {action statements}". 在這個例子中, 一共有兩個語句:"1"和"{print ""}". 在pattern-action語句中, 模式或者動作都可以省略掉. 如果省略模式, 動作會應用于輸入中的每一行. 而省略掉的動作等價于'{print }'. 因此, 這行程序可以轉換為:
awk '1 { print } { print "" }' filname.ext
動作只會應用于匹配模式的行, 也就是說, 模式為真(true). 由于'1'永遠為真, 因此這行程序可以進一步轉換為兩個print語句:
awk '{ print } { print "" }' filname.ext
Awk中的每個print語句都會跟一個ORS-輸出記錄分隔符(Out Record Separator)變量, 默認情況下是一個換行符. 第一個不帶參數的print語句等價與"print $0", 這里$0表示輸入的整行內容.第二個print語句什么都不輸出, 但是由于每個后面都有一個ORS, 因此它實際輸出一個換行. 好了, 現在每一行都變成雙倍間距了.
3.2 另一種行間距加倍的方式
awk 'BEGIN { ORS="\n\n" }; 1' filname.ext
BEGIN是一個特殊的模式, 它不是用來匹配輸入. 而是在任何輸入被讀入之前執行. 這行程序通過將ORS設定為兩個換行來使文件間隔加倍. 就像我在前面提到過的, 語句"1"會被轉換為"{print}", 并且每個print語句都會以ORS變量作為結尾.
3.3 使文件行間距加倍, 并且兩行文本之間不會有多于一行的空行.
awk 'NF { print $0 "\n" }' filname.ext
這個單行程序使用另一個特殊的變量:NF-Number of Fields. 它保存當前行被分割后的字段數. 例如, "this is a test"被分割為4部分, 因此NF的值為4. 空行""不能被分割為任何片段, 這時NF的值為0. 使用NF作為模式可以有效的過濾空行. 這個單行程序的意思是:如果包含任意數目的字段, 則將整行內容輸出, 然后再輸出一個換行.
3.4 使行間距增加兩倍.
awk '1; { print "\n" }' filname.ext
這個單行程序跟上一個很類似. '1'被轉換為'{ print }', 轉換后的結果為:
awk '{ print; print "\n" }' filname.ext
它輸出一行的內容, 然后輸出一個換行, 后面再跟一個ORS, 默認情況下ORS也是一個換行.
4 計數和計算
4.1 單獨為每個文件的行計數.
awk '{ print FNR "\t" $0 }' filname.ext
這個Awk程序在每行前面添加FNR–File Line Number–和一個tab(\t).FNR變量單獨保存每個文件的當前行行號. 例如, 如果用這個單行程序處理兩個文件,其中一個有10行,另一個12行, 那么它會從1到10 給第一個文件的各行編號, 然后重新從1開始, 將第二個文件的各行依次編號為1到12. 也就是說在處理兩個文件中間, FNR會被重置.
4.2 給所有文件的行計數.
awk '{ print NR "\t" $0 }' filname.ext
這個程序跟上一個的唯一不同在于:這兒用的是NR(Line Number)變量, 它在文件之間不會被重置. 它會為輸入的所有行計數. 例如, 如果用這個程序來處理上個例子中的兩個文件, 它會將文件中的行依次編號為1到22(10+12).
4.3 用精心設計的方式計數.
awk '{ printf("%5d : %s\n", NR, $0) }' filname.ext
這個單行程序使用printf()函數來以定制的格式計數. 它就像我們常見的printf()函數一樣帶有格式參數. 注意printf()輸出的時候并不會附加ORS, 因此我們必須顯式地輸出換行符(\n). 這個程序以右對齊的方式輸出行號, 后面跟一個空格和分號, 然后是輸入行.
4.4 只對文件中的非空行計數.
awk 'NF { $0=++a " :" $0 }; { print }' filname.ext
Awk變量是動態的, 它們產生于第一次被使用的時候. 這個單行程序中, 當輸入行非空的時候, 先將a加1, 然后將a的值添加到當前行的前面一起輸出.
4.5 計算文件行數(模擬wc -l)
awk 'END { print NR }' filname.ext
END是另一個不會匹配輸入行的模式. 它在所有輸入都處理完之后執行. 這個單行程序在處理完所有輸入之后輸出NR的值. NR中保存處理的總行數.
4.6 輸出每行中各個字段的和.
awk '{ s = 0; for (i = 1; i <= NF; i++) s = s+$i; print s }' filname.ext
Awk有一些C語言的特征, 例如for(;;) {…}循環. 這個單行程序循環處理輸入行的各個字段(一行中有NF個字段), 將各個字段的值加到變量's'. 然后將累計值s輸出, 接著再處理下一行.
4.7 輸出所有行中各個字段的和.
awk '{ for (i = 1; i <= NF; i++) s = s+$i }; END { print s }' filname.ext
這個程序跟4.6中的差不多, 區別在于它輸出所有字段的和. 要注意它是怎樣沒有將變量's'初始化為0. 由于變量是動態產生的, 因此不需要初始化.
4.8 將每個字段替換為它的絕對值.
awk '{ for (i = 1; i <= NF; i++) if ($i < 0) $i = -$i; print }' filname.ext
這個單行程序使用了兩個其他的C語言特性, 也就是if(…){…}語句和省略花括號. 它循環處理一行輸入的所有字段, 檢查是否有字段的值小于0. 如果有字段的值小于0, 就對其取反, 是字段值變為正數. 字段可以通過一個變量間接的訪問. 例如, i=5; $i='hello', 將第五個字段的值置為'hello'.下面是重寫過的程序, 跟上面的相同, 不過為了清晰起見添加了花括號. 在每行輸入的所有字段都被它們的絕對值替換之后, 'print'語句就會執行.
awk '{for (i = 1; i <= NF; i++) {if ($i < 0) {$i = -$i;}}print}' filname.ext
4.9 記錄輸入文件中所有字段的數目.
awk '{ total = total + NF }; END { print total }' filname.ext
這個單行程序匹配所有的輸入行, 并且將每行中的字段數加起來. 已處理過的輸入的字段數會保存在變量total中. 一旦輸入被處理完, 特殊的模式'END{…}'就會被執行, 它會輸出總的字段數.
5 特定行的刪除/輸出
5.1 輸出包含單詞"Beth"的行的總行數.
awk 'Beth { n++ }; END { print n+0 }' filname.ext
這個單行程序有兩個pattern-action語句. 第一個是'Beth { n++ }'. 在兩個斜線之間的模式是一個正則表達式. 它匹配所有包含模式"Beth"的行(不一定非得是單詞"Beth", 也可以是"Bethe"或者"theBeth333"). 當某行輸入匹配的時候,變量'n'增加1. 第二個pattern-action語句是'END {print n+0}'. 它在文件處理完之后執行. 注意語句'print n+0'中的'+0'. 在沒有任何輸入行匹配的情況下, 它會強制輸出'0'(此時'n'是未定義的). 假設沒有'+0', 那就會輸出一個空行.
5.2 查找第一個字段的值(數字)最大的行.
awk '$1 > max { max=$1; maxline=$0 }; END { print max, maxline }' filname.ext
這個單行程序始終保存輸入行中第一個字段的最大值(在變量max中)以及相應的行(在變量maxline中). 一旦處理完所有的行, 就將最大值和對應行輸出.
5.3 輸出每行中的字段數, 以及對應的行.
awk '{ print NF ":" $0 } ' filname.ext
這個單行程序僅僅輸出預定義的變量NF-Number of Fields, 它保存輸入行中的字段數, 然后輸出分號和當前行.
5.4 輸出每行的最后一個字段.
awk '{ print $NF }' filname.ext
Awk中的字段不一定非得通過常量引用. 例如, 類似'f=3; print $f'的代碼會輸出第三個字段. 這個單行程序輸出第NF個字段. $NF就是一行中的最后一個字段.
5.5 輸出最后一行的最后一個字段.
awk '{ field = $NF }; END { print field }' filname.ext
這個單行程序始終將最后一個字段的值保存在變量'field'中. 一旦處理完所有的行, 變量field中保存的就是最后一行的最后一個字段, 然后會將它輸出.
5.6 輸出字段數多于4的行.
awk 'NF > 4' filname.ext
這個單行程序省略了action語句. 就像我在3.1中提到的, 省略的action語句等價于'{print}'.
5.7 輸出最后一個字段的值大于4的行.
awk '$NF > 4' filname.ext
這個單行程序跟5.4類似. 它通過NF引用最后一個字段. 如果它大于4, 則將它輸出.
Date: 2008/12/10 11:09:39