AWK 從不會到入門 Skr Skr

awk 含義: Aho, Weinberger & Kernighan interpreted language,是由這兩我的共同發明的。html

這不是一個工具,「這是一個偉大、有着奇怪的名字的語言」。bash

發音:awk [ɔk]函數

基本用法

awk '{ print }' /etc/passwd # 至關於 "cat /etc/passwd"
awk '{ print $0 }' /etc/passwd # 與前面一行語句等價
  1. 花括號裏的 print 函數用於將匹配到的每一行逐行打印出來
  2. print 和 print $0 等價

Multiple Fields

awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd # 打印第一列和第三列的字符串,並以製表符分隔
awk -F":" '                    \
BEGIN{                         \
    print "username\tuid"     \
}; {                         \
    print $1 "\t" $3         \
}' /etc/passwd # 同上,可是將 username 和 uid 放置表頭打印,看起來更美觀
  1. -F 參數表示以 ":" 做爲分隔符
  2. BEGIN 表示會在匹配第一行以前執行,所以很適合用做打印表頭

Block(pattern-action,man-page 的說法)

如你所見,AWK 單引號內的腳本被稱做「AWK Script」,腳本內由一個或多個花括號對組成,每個花括號被叫作 Block。像「基本用法」裏的單個 Block 是最簡單的 Block。AWK 是按行處理文本的,每拿到一行字符串,都會將 AWK Script 內的 Block 按序匹配,匹配成功時則執行 Block 內的代碼工具

awk '                         \
BEGIN {                     \
    FS = ":";                 \
    count = 0;                \
    print "username\tuid";     \
}                             \
{                            \
    count += 1;                \
    print $1 "\t" $3        \
}                             \
END {                        \
    print "Totals:" count     \
}' /etc/passwd

awk -f block.awk /etc/passwd # 若是你嫌在命令行裏輸入麻煩,還能夠將腳本以文件的方式執行

以上代碼共有三個 Block,awk 每拿到一行字符串都會從前到後匹配這三個 Block,如若匹配上,則執行裏面的代碼。BEGIN 裏定義了分隔符 FS(Field Seperator),這和以前的「Multiple Fields」裏的 -F 選項是徹底等價的效果。此外還定義了一個記錄行數的變量 count,第二個 Block 沒有定義任何條件,所以從第一行到最後一行都會被匹配成功!END 行最後輸出行數。性能

awk 會在待匹配的文本第一行以前和最後一行以後各插入一行空行BEGINEND Block 則只有當分別匹配到這兩行空行的時候纔會執行。BEGIN 和 END 就好像語法糖同樣,只是執行的時間一個最靠前,一個最靠後,其它與正常的 Block 並沒有任何區別ui

正由於 BEGIN 這樣的特性,所以你能夠將變量定義、打印表頭等初始化工做放在這裏;END 適合用來作一些總結性的操做,打印行數、總字符數、行平均長度等等。命令行

帶條件的 Block

awk '                     \
BEGIN { x=0 }             \
/^$/  { x=x+1 }         \
END   { print "I found " x " blank lines. :)" }'  file

以上代碼用於打印文件中全部的空行,若是你是 JSer 或者 Perl-er,可能會對 /^$/ 比較熟悉,借鑑關係鏈:JavaScript <- Perl <- awk。awk 能夠說是這套正則表示的鼻祖。code

其實在每個 Block 以前都存在一個隱含條件,無條件的 Block 會匹配全部文件中存在的行(不包括 awk 插入的兩行空行)。當 Block 被附加條件後,只有當條件爲真時纔會執行。htm

awk '( $1 == /[0-9]+\.[0-9]*/ ) && NR%3 { print }' file

以上僅僅打印以浮點數開頭且行數不爲 3 的倍數的行,因爲 AWK Script 是弱類型的,所以像 0 會被判爲 false。在 awk 裏,Record 就是行,所以 NR 表示 Number of Record。ip

在 AWK Script 中,全部變量的類型都是 string,當對 string 進行算術運算(好比加減乘除)時,awk 會將string 解析爲 number,若是不是合法的 number,則視爲 0;而後再進行算術運算。

爲了更好的表現 BEGIN 的含義,欣賞下面的代碼:

awk ' !/^$/ && NR <= 100 {          \
    if (NR == 1) {                  \
        FS = ":";                     \
    }                               \
    # 過濾掉註釋和 NF 小於 3 的行      \
    if (!($1 ~ /^#/) || NF >= 3) {  \
           print $1 ":" $3;              \
    }                               \
}' /etc/passwd

整個代碼只有一個 Block,這個 Block 只有在非空的行以及行數小於 100 裏才能執行,當 awk 拿到第一行(NR == 1)時,定義了分隔符 FS(Field Seperate),這本來是在 BEGIN Block 中完成的(由於這個例子中的第一個 if 每次都會被執行,浪費性能)。第二個 if 過濾掉註釋語句且 NF(Number of Field)大於 3 才執行,在 awk 中,NF 表示每一行被 FS 分隔後獲得的 Field 的數量,至關於 NF = line.split(FS)。這個例子說明了 BEGIN 和 END 僅僅是語法糖,在本質上和普通的 Block 沒有任何區別。像 NF 和 FS 這一類的是 awk 的自帶變量,變量能夠出如今 Block 的匹配條件裏,同時也能夠修改自帶變量的值來改變 awk 的行爲

函數

awk 靠 AWK Script 腳本工做,爲了方便,awk 提供了一些內置函數,以及容許你定義函數的功能。下面是一個找出 /etc/passwd 文件中第三列最大的數所對應的行。

function find_max(n1, n2)
{
    if (n1 > n2) {
        return n1;
    }
    return n2;
}

BEGIN {
    FS = ":";
    max = -1;
}
{
    if (NF < 3) {
        next
    }
    # 第一行
    max = $3;
    mR = NR;
    while (getline == 1) {
        _max = find_max(max,$3);
        if (max < _max) {
            max = _max;
            mR = NR;
        }
    }
}
END {
    print "max is:" max;
    print "NR is:" mR;
}

而後執行 awk -f ./max.awk /etc/passwd

find_max 是自定義函數,調用的時候要帶上圓括號,像 getline 和 next 則是內建函數,內建函數的調用不須要帶圓括號。其中 next 用來跳過本次執行,主要用於處理文件開頭的一些註釋;getline 用來獲取下一行,getline 會把下一行賦值給 $x(x 表明數字)。若是沒有下一行就返回 0,while 循環結束。getline 改變了 awk 的行爲,awk 從開始到結束只執行了一次 Block。

還有一些數學函數(如 sin、cos、sqrt)以及字符串處理函數(length、sprintf 格式化字符串)屬於內建函數。

總結

awk 可玩性極強,藉助 AWK Script,你能夠用它模擬其它常見的文本處理工具(誰又會這麼作呢?)

參考連接

相關文章
相關標籤/搜索