guest@blog.cmj.tw: ~/posts $

AWK


AWK

another language you need to know ~

AWK 是一個強大的文字處理腳本語言。在 1970s 的時候 AWK 在 貝爾實驗室 被設計出來處理文字並產生報表, AWK 本身具有讀取文字、正規表示法、數學運算、邏輯控制、自定義函數以及內建變數與函數。AWK 將讀入的文件, 每一行是為一個紀錄 (record),且將每一行分為若干的域 (field),所以第一行的第一個自則會被視為第一個域。 AWK 會根據每一個紀錄來比對是否符合模式 (pattern),並且針對符合的模式做相對的動作。

在簡單的 AWK 應用中,我會拿來計算 nginx 中存取紀錄 (access.log) 中的 IP 數量:

cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c

在這個例子中,awk 主要的用途就是將每一行的第一個字顯示出來,因此會使用 $1 來代表第一個 field 並且用 print 將結果顯示出來。在 awk 的語法當中,是利用大括號來代表預計執行的動作,並且在前方指定預計的模式:

condition { action }

語法 (Grammar)

在 AWK 當中支援若干的基本語法以及內建的變數與函數,在語法當中支援了條件判斷 (if-else statment)、 迴圈 (while、do-while、for statement)、函數宣告 (function statment) 以及常用的語句 (print、next、exit)。 詳細的內容,可以參考 AWK 的 man page:

if( expression ) statement [ else statement ]
while( expression ) statement
for( expression ; expression ; expression ) statement
for( var in array ) statement
do statement while( expression )
break
continue
{ [ statement ... ] }
expression              # commonly var = expression
print [ expression-list ] [ > expression ]
printf format [ , expression-list ] [ > expression ]
return [ expression ]
next                    # skip remaining patterns on this input line
nextfile                # skip rest of this file, open next, start at top
delete array[ expression ]# delete an array element
delete array            # delete all elements of array
exit [ expression ]     # exit immediately; status is expression

## Built-In Variable
FS			regular expression used to separate fields; also settable by option -Ffs.
NF			number of fields in the current record
NR			ordinal number of the current record
FNR			ordinal number of the current record in the current file
FILENAME	the name of the current input file
RS			input record separator (default newline)
OFS			output field separator (default blank)
ORS			output record separator (default newline)
OFMT		output format for numbers (default %.6g)
SUBSEP		separates multiple subscripts (default 034)
ARGC		argument count, assignable
ARGV		argument array, assignable; non-null members are taken as filenames
ENVIRON		array of environment variables; subscripts are names.

原始碼 (Source Code)

從 [GNU AWK] 的下載 4.1.3 版本的 source code 來分析,可以發現最後產生 gawk 的編譯指令, 總共用到 28 個 .o 檔。可以發現有若干的檔案本身具有特定的目的,像是 regex.o 跟 re.o 從名稱來看是用來處理正規表示法,而 random.o 則是用提供 built-in 的 random 函數使用。

gcc -g -O2 -DNDEBUG -o gawk \
    array.o awkgram.o builtin.o cint_array.o command.o debug.o dfa.o \
    eval.o ext.o field.o floatcomp.o gawkapi.o gawkmisc.o getopt.o   \
    getopt1.o int_array.o io.o main.o mpfr.o msg.o node.o profile.o  \
    random.o re.o regex.o replace.o str_array.o symbol.o version.o	 \
        -lreadline

從一開始的 main.c #224 行開始,可以看到在程式一直行前先判斷是否設定了環境變數 TIDYMEM 並且當有設定時且當下系統支援 mtrace 則會啟用 mtrace 來追蹤紀錄記憶體使用的狀況。

    /* do these checks early */
    if (getenv("TIDYMEM") != NULL)
        do_flags |= DO_TIDY_MEM;

#ifdef HAVE_MCHECK_H
#ifdef HAVE_MTRACE
    if (do_tidy_mem)
        mtrace();
#endif /* HAVE_MTRACE */
#endif /* HAVE_MCHECK_H */

接下來處理若干個 single 狀況,並且交由 cachesig 這個 function 來處理遇到 signal 的狀況。 而呼叫 emalloc 這個包裝過後的 malloc 函數:他可以檢查請求的大小是否為 0 以及是否有請求失敗的狀況。 在預處理完環境後利用 parse_program 來處理讀進來的指令並且產生若干個指令 (isntructions), 而 mk_program 就是用來產生一個指令集 (a single list of instructions) 的函數。 最後利用 interpret 來執行程式碼:其中會使用到 h_interpret 以及 r_interpret 兩種。按照註解的描述, 後者效能會快上約 5%。