AWK是一門解釋型的編程語言,它的名字來源于它的三位作者的姓氏:Alfred Aho,Peter Weinberger和Brian Kernighan。AWK能夠應(yīng)用于廣泛的計(jì)算和數(shù)據(jù)處理任務(wù)。所有的GNU/linux發(fā)行版都自帶GAWK,即GNU AWK,是AWK的擴(kuò)展并且與AWK完全兼容。
和上面講到的sed命令類似,AWK逐行讀取輸入流中的內(nèi)容,并對讀取的行執(zhí)行所有命令,如此循環(huán),直到輸入流結(jié)束。
本文基于GAWK進(jìn)行介紹,因?yàn)镚AWK比原生AWK使用更普遍些。
AWK命令格式如下:
awk [options] 'PRogram' input-file1 input-file2 ...
或者
awk [options] -f program-file input-file1 input-file2 ...
第一種格式中,awk從file中獲取輸入流,然后執(zhí)行單引號內(nèi)的程序。第二種格式則是從文件program-file中獲取將要執(zhí)行的程序。
上述AWK命令的program部分的結(jié)構(gòu)可分為三塊:BEGIN、BODY和END。
BEGIN:在AWK命令的一開始執(zhí)行的動作,它只執(zhí)行一次,可以把變量初始化放在這里。注意,BEGIN部分是可選的,并且一個(gè)AWK命令中可以有多個(gè)BEGIN塊。注意,如果有-v選項(xiàng)的賦值操作,則-v的操作在BEGIN之前。
BEGIN塊的寫法為:BEGIN{…}
BODY:程序主體,對輸入流的每一行執(zhí)行動作。如果存在BEGIN或END,則這部分是可選的。一個(gè)AWK命令中可以有多個(gè)BODY塊。
BODY塊的寫法為:{…}
END:在AWK命令的最后執(zhí)行的動作,它只執(zhí)行一次。注意,END部分是可選的,并且一個(gè)AWK命令中可以有多個(gè)END塊。注意,使用END { }塊時(shí),awk的file參數(shù)不能省略。
END塊的寫法為:END{…}
例如下面的命令:[root@Ubuntu]awk_test:$ awk 'BEGIN{print "Output start!"}; {print}; END{print "Output done!"}' awk_test.txt Output start!USER PID %MEM VSZ rss STAT START TIME COMMANDroot 1 0.1 3652 1916 Ss Jan07 0:03 /sbin/initroot 2 0.0 0 0 S Jan07 0:00 [kthreadd]root 3 0.0 0 0 S Jan07 0:02 [ksoftirqd/0]root 26 0.0 0 0 S Jan07 0:40 [kswapd0]user 495 0.1 3588 1092 Ss Jan07 0:00 /sbin/udevd --daemonuser 860 0.0 3584 908 S Jan07 0:01 /sbin/udevd --daemonuser 1137 0.0 4520 776 S Jan07 0:00 smbd -Fuser 1550 0.1 4521 1816 Ss Jan07 0:15 nmbd -DOutput done!可以看到這條命令在命令的開始打印一行輸出“Output start!”,然后對文件中的每一行內(nèi)容執(zhí)行print操作,最后又打印出一行輸出“Output done!”。
從句法結(jié)構(gòu)上來講,program由一條條規(guī)則組成,每條規(guī)則由模式和動作組成,即模式匹配后執(zhí)行相應(yīng)的動作。動作放在花括號內(nèi)以與模式區(qū)分。所以,program一般的格式是這樣的:
pattern { action }
pattern { action }
…
那么,下面這條命令(打印長度大于80字符的行):
awk 'length($0) > 80' data可以看到這條awk命令只有pattern而沒有action部分。如果沒有action部分,則執(zhí)行默認(rèn)動作:打印整個(gè)record。
也可以把program寫到文件里(不用加單引號),通過AWK的第二種格式來執(zhí)行。[root@ubuntu]awk_test:$ cat progfile BEGIN{print "Output start!"};{print};END{print "Output done!"};[root@ubuntu]awk_test:$ awk –f progfile awk_test.txt為了方便后期維護(hù),建議將AWK的程序文件以.awk作為后綴名。
另外,可以利用Shell的#!機(jī)制,將progfile內(nèi)容改為:#!/usr/bin/awk –fBEGIN{print "Output start!"};{print};END{print "Output done!"};這樣的話,執(zhí)行./progfile awk_test.txt即可。(通過type awk可以知道系統(tǒng)中awk命令的位置。)
注:使用#!機(jī)制的話,執(zhí)行的shell命令./progfile實(shí)際上是執(zhí)行:"#!后面的命令" +"./progfile腳本" + "./progfile腳本的參數(shù)"。另外,這種寫法,awk后面最多只能跟一個(gè)參數(shù);并且ARGV[0]的值在不同系統(tǒng)上可能表現(xiàn)不同,比如可能被解釋為awk或/usr/bin/awk或./progfile。
比較特別的,如果要在program內(nèi)使用或打印單引號,可以用其ASCII碼'/47'表示。或者把程序?qū)懺谖募校@樣就不用擔(dān)心單引號和program外圍的單引號混淆的問題。注意,在單引號中,反斜杠后接一個(gè)字符,會被解釋成這個(gè)字符的字面意思,即和不加反斜杠是一樣的含義。
你也可以通過下面兩種方法來打印單引號:
awk 'BEGIN { print "Here is a single quote <'"'"'>" }'或
awk 'BEGIN { print "Here is a single quote<'/''>" }'AWK選項(xiàng)
-f program-file 執(zhí)行文件中的程序,前面已講過。Program-file可以有多個(gè),通常用來將通用的代碼或函數(shù)做成庫,以實(shí)現(xiàn)代碼復(fù)用。
環(huán)境變量AWKPATH用來指定-f的搜索路徑,如果不指定AWKPATH,則默認(rèn)搜索“.:/usr/local/share/awk”,可以通過修改AWKPATH或ENVIRON["AWKPATH"]來修改搜索路徑,每個(gè)路徑之間用冒號隔開(.或::都可以表示當(dāng)前路徑)。如果-f選項(xiàng)后面跟的是包含“/”的文件名,就不會去額外搜索路徑了。
-v var=value 變量賦值,在BEGIN之前進(jìn)行。例如,定義變量name,并賦值為“jason”:[root@ubuntu]awk_test:$ awk -v name=jason 'BEGIN{printf("name=%s/n", name)}'name=jason注意變量的引用不需要加“$”,實(shí)際上AWK的語法很多跟ANSI C的語法類似。“$”在AWK中是用來引用field的,后面會講到。
-F fs 使用fs作為分隔符(默認(rèn)是空格)例如,打印/etc/passwd文件中的用戶名一列:[root@ubuntu]awk_test:$ cat /etc/passwd | awk -F ':' '{print $1}'rootdaemonbinsyssyncgames --compat 或 --traditional 使用原生awk,不會識別gawk的擴(kuò)展。例如,原生awk允許在while/for/do循環(huán)外面使用continue和break語句,這會被認(rèn)為和next語句意思相同。Gawk添加--traditional則可以使用這一特性。 --dump-variables[=file] 輸出排好序的AWK內(nèi)置的全局變量到文件(若不指定文件,則默認(rèn)為awkvars.out)這個(gè)選項(xiàng)可以查看當(dāng)前可用的內(nèi)置全局變量。在編程的時(shí)候要注意,不要定義和這些內(nèi)置變量重名的變量。
--non-decimal-data 識別輸入流中的十六進(jìn)制和八進(jìn)制例如,將下面文件中的數(shù)字相加求和:[root@ubuntu]awk_test:$ cat number.txt 0x120x3201210[root@ubuntu]awk_test:$ awk '{sum+=($1)}; END{print sum}' number.txt 22[root@ubuntu]awk_test:$ awk --non-decimal-data '{sum+=($1)}; END{print sum}' number.txt 88可以看到,如果不加--non-decimal-data選項(xiàng),就只識別出十進(jìn)制的12和10。
不過man gawk中說“Use this option with great caution!”,一方面這個(gè)選項(xiàng)可能破壞舊的程序,另一方面這個(gè)選項(xiàng)可能在以后被摒棄。
--profile[=prof_file] 生成awk命令的profile文件這個(gè)選項(xiàng)以優(yōu)雅的格式將awk命令保存到文件,如果不指定文件名,則默認(rèn)為awkprof.out。[root@ubuntu]awk_test:$ awk --non-decimal-data --profile '{sum+=($1)}; END{print sum}' number.txt[root@ubuntu]awk_test:$ cat awkprof.out # gawk profile, created Sun Jan 8 23:20:48 2017 # Rule(s) { sum += $1 } # END block(s) END { print sum }如果使用pgawk執(zhí)行命令,則還會顯示每條語句以及每個(gè)函數(shù)的調(diào)用次數(shù)。
--re-interval 在正則表達(dá)式中支持間隔表達(dá)式傳統(tǒng)的awk不支持間隔表達(dá)式,必須加上--re-interval選項(xiàng)或者--posix選項(xiàng)才能使用。
-e program-text 或 --source program-text program-text為awk的program源碼,這個(gè)選項(xiàng)允許將文件中的源碼和命令行中的源碼混合使用。在需要引用自定義的庫函數(shù)時(shí)就可以使用該選項(xiàng),例如:awk -f func_test.awk --source 'BEGIN{printadd_INT(1,2)}' awk_test.txt
這個(gè)例子中,func_test.awk里面定義了函數(shù)add_INT(),這里將-f指定的文件程序和--source指定的命令行程序混合在了一起。
-E file 或 --exec file 意義和-f選項(xiàng)相同,不過有兩點(diǎn)區(qū)別:命令行中的其他選項(xiàng)都直接傳給awk,而awk先處理其他選項(xiàng)和參數(shù),最后才處理--exec選項(xiàng);另外,不允許“var=value”形式的變量賦值。這個(gè)選項(xiàng)應(yīng)該在#!開頭的腳本里使用,例如:
#! /usr/local/bin/gawk -E awk program here …這個(gè)選項(xiàng)可以防止向腳本里傳遞參數(shù),因?yàn)樗械膮?shù)都先被awk識別并處理了。
--include source-file--load ext這兩個(gè)選項(xiàng)都是針對引用函數(shù)庫的,也可以在文件中使用@include和@load來引用庫文件。不過在我所用系統(tǒng)的gawk不支持這兩個(gè)參數(shù)以及相應(yīng)的AWKLIBPATH環(huán)境變量,我就不介紹了。我們就使用-f來引用庫文件吧,只是-f的文件里面可以是任何程序內(nèi)容,并不是只針對庫文件而設(shè)計(jì)的。
-- 標(biāo)記選項(xiàng)的結(jié)束這告訴awk選項(xiàng)部分已經(jīng)結(jié)束,可以用來傳遞以“-”開頭的參數(shù),而不被誤認(rèn)為是選項(xiàng)。
AWK的變量、Records和Fields
AWK的變量是動態(tài)生成的,在第一次被使用的時(shí)候開始存在。變量的值可以是下列數(shù)據(jù)類型:浮點(diǎn)型、字符串類型和一維數(shù)組。甚至一個(gè)變量既可以是浮點(diǎn)型也可以是字符串類型,這取決于代碼中如何使用它。
AWK會將“var=value”形式的參數(shù)認(rèn)為是變量賦值,例如下面這個(gè)命令,awk將“var=2”和“var=1”認(rèn)為是給var賦值,而不是一個(gè)文件名,這個(gè)過程在awk順序處理參數(shù)列表時(shí)進(jìn)行的。
awk 'var == 1 {print 1} var == 2 { print 2}' var=2 awk_test2.txt var=1 awk_test1.txtRecords
一個(gè)record就是awk認(rèn)為的一行輸入,對一個(gè)輸入流,默認(rèn)以換行符分隔。不過可以通過內(nèi)置變量RS來修改。例如,把RS賦值為Jan07:
[root@ubuntu]awk_test:$ awk -v RS=Jan07 '{print}' awk_test.txt USER PID %MEM VSZ RSS STAT START TIME COMMANDroot 1 0.1 3652 1916 Ss 0:03 /sbin/initroot 2 0.0 0 0 S 0:00 [kthreadd]root 3 0.0 0 0 S 0:02 [ksoftirqd/0]root 26 0.0 0 0 S 0:40 [kswapd0]user 495 0.1 3588 1092 Ss 0:00 /sbin/udevd --daemonuser 860 0.0 3584 908 S 0:01 /sbin/udevd --daemonuser 1137 0.0 4520 776 S 0:00 smbd -Fuser 1550 0.1 4521 1816 Ss 0:15 nmbd -D看效果就知道是什么意思了。注意“Jan07”本身沒有打印出來。
如果RS被賦值為單個(gè)字符,則這個(gè)字符就是records的分隔符;如果被賦值為多個(gè)字符,那RS實(shí)際上是一個(gè)正則表達(dá)式。
如果RS被賦值為空,則以空行作為record的分隔符。fields
AWK會把一個(gè)record再分隔成一個(gè)個(gè)的field依次進(jìn)行處理,以變量FS的值作為分隔符。
如果FS被賦值為單個(gè)字符,則這個(gè)字符就是fields的分隔符;如果被賦值為多個(gè)字符,那就是一個(gè)正則表達(dá)式;如果FS是空,則record中的每個(gè)字符都是分隔符。
注意,如果FS是空格,則多個(gè)空格、tab和換行,都會被認(rèn)為是分隔符。
一個(gè)record中的各個(gè)field可以通過各自的位置來引用,依次為$1,$2,$3,...。而$0表示整個(gè)record。
如果給FIELDWIDTHS變量賦值為一個(gè)數(shù)值列表(以空格隔開),如下面的例子,record就會按照字符串長度來分隔fields而不是按照FS的值。這時(shí)FS變量會被忽略。[root@ubuntu]awk_test:$ awk 'BEGIN{FIELDWIDTHS="2 4 4 4"}{print $1"||"$2"||"$3"||"$4}' awk_test.txt US||ER || || PIDro||ot || || 1ro||ot || || 2ro||ot || || 3ro||ot || || 26us||er || || 495us||er || || 860us||er || ||1137us||er || ||1550如果給FS重新賦值,就會切回到按照FS分隔fields。
NF變量用于獲取當(dāng)前record共分了多少了fields。你可以給NF、$0以及某個(gè)field賦值,那么相應(yīng)record會更新并重新劃分fields。例如:[root@ubuntu]awk_test:$ awk '{NF-=1; if (FNR!=1) $1="json"; print}' awk_test.txt USER PID %MEM VSZ RSS STAT START TIMEjson 1 0.1 3652 1916 Ss Jan07 0:03json 2 0.0 0 0 S Jan07 0:00json 3 0.0 0 0 S Jan07 0:02json 26 0.0 0 0 S Jan07 0:40json 495 0.1 3588 1092 Ss Jan07 0:00 /sbin/udevdjson 860 0.0 3584 908 S Jan07 0:01 /sbin/udevdjson 1137 0.0 4520 776 S Jan07 0:00 smbdjson 1550 0.1 4521 1816 Ss Jan07 0:15 nmbd這個(gè)例子中,把NF減1,則原來的每個(gè)record的最后一個(gè)field就沒有打印出來。FNR表示當(dāng)前已經(jīng)輸入了幾個(gè)record,例子中將文件除了第一行之外的第一個(gè)field的值都改成了“jason”。內(nèi)置變量
AWK有一些內(nèi)置的全局變量可以直接使用,會使編程更方便。
內(nèi)置變量 | 含義 |
ARGC | Awk命令行的參數(shù)個(gè)數(shù)(不包括選項(xiàng)和選項(xiàng)的值以及program部分),awk自身是第0個(gè)參數(shù)。 |
ARGV | Awk的參數(shù)列表,共ARGC個(gè)元素。 |
ARGIND | 當(dāng)前正在處理的文件處于ARGV數(shù)組中的index。 |
BINMODE | 是否使用binmode打開文件。1("r"),2("w"),3("rw")分別表示輸入文件、輸出文件、所有文件需要使用binmode打開。 |
CONVFMT | 數(shù)字的格式化類型,默認(rèn)是"%.6g"。 |
ENVIRON | 保存當(dāng)前所有環(huán)境變量的數(shù)組。這個(gè)數(shù)組是以環(huán)境變量名作為下標(biāo)的,例如ENVIRON["HOME"]的值是"/root"。 |
ERRNO | 當(dāng)getline或close失敗,ERRNO會保存字符串形式的錯(cuò)誤描述。 |
FIELDWIDTHS | 一個(gè)以空格分隔的數(shù)值列表,設(shè)置這個(gè)值后,F(xiàn)S失效,record改以字符長度來劃分fields。 |
FILENAME | Awk當(dāng)前處理的文件的名字。注意,在BEGIN{ }中由于還沒開始處理文件,F(xiàn)ILENAME是空(除非被getline設(shè)置)。 |
FNR | 表示正在處理的文件目前已經(jīng)輸入了幾個(gè)record。 |
FS | Field分隔符,默認(rèn)是空格。 |
IGNORECASE | 是否忽略大小寫,默認(rèn)是0。用于控制正則表達(dá)式和字符串操作,例如會影響records和fields的分隔符。注意,數(shù)組下標(biāo)不受影響。 |
LINT | 如果為true,則對可能不兼容的awk命令提示warning,如果為fatal,則提示為錯(cuò)誤。默認(rèn)是0:不提示。 |
NF | 當(dāng)前record中的fields的數(shù)目。 |
NR | 已經(jīng)處理的record的數(shù)目(包括當(dāng)前record)。 |
OFMT | 輸出中數(shù)字的格式,默認(rèn)是“%.6g”。 |
OFS | 輸出中fields的分隔符,默認(rèn)是空格。例如下面的命令,就會以” : ”來打印fields(注意$1 $2 $3之間要加逗號): awk -v OFS=" : " '{print $1,$2,$3}' awk_test.txt |
ORS | 輸出中records的分隔符,默認(rèn)是新行。 |
PROCINFO | 一個(gè)包含正在執(zhí)行的awk命令自身信息的數(shù)組,以元素名作為下標(biāo),例如PROCINFO[“egid”],PROCINFO[“pid”]等,下面的命令可以列舉所有的元素: awk '{for (var in PROCINFO) print var;}' awk_test.txt |
RS | records分隔符,默認(rèn)是新行。 |
RT | record的結(jié)束符,通常被賦值為RS。 |
RSTART | match()匹配成功的子串的第一個(gè)字符在原始字符串中的index(從1開始)。0表示匹配失敗。 |
RLENGTH | match()匹配成功的子串的長度。-1表示匹配失敗(可以匹配空串,此時(shí)長度為0)。 |
SUBSEP | 數(shù)組下標(biāo)分隔符,默認(rèn)為/034即0x1C的ASCII符號。在訪問多維數(shù)組時(shí)有用。 |
TEXTDOMAIN | 文本域,本地化的時(shí)候用到。 |
對于FS,強(qiáng)調(diào)一個(gè)‘FS =" "’和‘FS = "[ /t/n]+"’的不同,這兩種FS都是將多個(gè)空格、tab或newline作為分隔符。但是前者會先將record中的前導(dǎo)空格和尾部空格剝掉,再決定如何分割fields。例如下面兩條命令的結(jié)果就不同:
[root@ubuntu]awk_test:$ echo ' a b c d ' | awk '{ print $2 }'b[root@ubuntu]awk_test:$ echo ' a b c d ' | awk 'BEGIN { FS = "[ /t/n]+" }{ print $2 }'A也可以據(jù)此特征來刪除前導(dǎo)空格:[root@ubuntu]awk_test:$ echo ' a b c d' | awk '{ print; $2 = $2; print }' a b c da b c d這條命令通過$2=$2,讓awk重新劃分fields,由于FS模式是空格,這時(shí)就會先把前導(dǎo)空格和尾部空格剝掉。
數(shù)組
AWK中的數(shù)組是關(guān)聯(lián)數(shù)組(鍵值對的形式),數(shù)組下標(biāo)放在方括號“[ ]”內(nèi),數(shù)組下標(biāo)可以是數(shù)值或字符串。可以用(expr, expr ...)來模擬多維數(shù)組的下標(biāo)。例如:i = "A"; j = "B"; k = "C"x[i, j, k] = "hello, world/n"上面定義了數(shù)組x下標(biāo)為"A/034B/034C"的值為"hello,world/n"。
操作符“in”可以用來測試下標(biāo)是否存在,例如:if (val in array)print array[val]如果是多維的下標(biāo),則寫成 if ((i, j) in array) print array[i, j]。
也可以用“in”來遍歷數(shù)組:
下面兩個(gè)例子,前者定義了一個(gè)一維數(shù)組,數(shù)組下標(biāo)是[“a,b”]的元素。后者用來定義多維數(shù)組,例子中定義了三維數(shù)組中下標(biāo)為[”a”][“,”][”b”]的元素。[root@ubuntu]awk_test:$ awk 'BEGIN{array["a"",""b"]=1;for(i in array) print i}'a,b[root@ubuntu]awk_test:$ awk 'BEGIN{array["a",",","b"]=1;for(i in array) print i}'a/034,/034b記住,數(shù)組的小標(biāo)總是字符串,如果以數(shù)值為下標(biāo),也會轉(zhuǎn)換成字符串,也就是說,a[17]的下標(biāo)是"17",并且和a[021]、a[0x11]是相同的下標(biāo)。
使用delete可以刪除一個(gè)數(shù)組元素,例如delete array[1],也可以delete array來刪除整個(gè)數(shù)組。
變量類型轉(zhuǎn)換
變量和fields可以是字符串或浮點(diǎn)數(shù)類型,一個(gè)變量和field的值是什么類型依賴于它處于的上下文,例如,如果用于數(shù)學(xué)表達(dá)式,就被當(dāng)為數(shù)值類型。
如果需要強(qiáng)制變量或field被當(dāng)作數(shù)值,則寫成var+0。如果需要強(qiáng)制被當(dāng)作字符串,則連接一個(gè)空串,例如var ""。
雖然所有的數(shù)值都是浮點(diǎn)型的,但是對于一個(gè)整數(shù),轉(zhuǎn)換成字符串的結(jié)果就是整型,例如12會被轉(zhuǎn)換為“12”而不是“12.00”。
在比較兩個(gè)變量var1和var2時(shí),如果兩個(gè)變量都是浮點(diǎn)型,則按照數(shù)值來比較;如果一個(gè)浮點(diǎn)型,另一個(gè)是字符串,則按照數(shù)值來比較,這時(shí),字符串變量被當(dāng)做“數(shù)值字符串”,例如("12"==12)的值為1;如果兩個(gè)變量都是字符串,則按照字符串來比較。
注意,只有在處理用戶輸入時(shí),才會將形如“57”這種看起來像數(shù)值的字符串認(rèn)為是“數(shù)值字符串”,例如getline的輸入、FILENAME、ARGV的成員、ENVIRON的成員以及split()產(chǎn)生的數(shù)組元素。其他情況下,則只是一個(gè)字符串常量。
未初始化的變量,會有兩個(gè)默認(rèn)的初始值:數(shù)值0以及空字符串""。八進(jìn)制和十六進(jìn)制
gawk識別八進(jìn)制和十六進(jìn)制,例如011、0x16。
字符串常量
字符串常量是指雙引號內(nèi)的字符序列。在字符串中,可以包含轉(zhuǎn)義字符,如下:
轉(zhuǎn)義字符 | 含義 |
// | 字面含義的反斜杠 |
/a | “alert”字符,通常是ASCII中的BEL |
/b | backspace,回車 |
/f | form-feed,換頁符 |
/n | newline,換行符 |
/r | carriage return,回車 |
/t | horizontal tab |
/v | vertical tab |
/xhex | 十六進(jìn)制數(shù)表示的ASCII碼字符。例如/x2A表示ASCII中的“*”號 |
/ddd | 一個(gè)、兩個(gè)或三個(gè)數(shù)字組成的八進(jìn)制數(shù)值表示的ASCII碼字符。例如/052表示ASCII中的“*”號,注意/52同/052相同 |
/c | 字面含義的字符c,例如需要使用原意的*號,則需要寫成/*。 |
AWK是一個(gè)line-oriented的語言,對每一行(record),先進(jìn)行模式匹配,再執(zhí)行動作。動作的語句放在花括號{ }里面。如果模式為空,則對所有record執(zhí)行動作,例如 { print } ,就是無條件的打印整個(gè)record。
用“#”對一行程序進(jìn)行注釋,直到換行則注釋內(nèi)容結(jié)束。
正常情況下,一個(gè)完整的語句以換行結(jié)束,但一個(gè)語句也可以寫成多行,如果一個(gè)語句在“,”、“{”、“?”、“:”、“&&”或“||”處換行,則認(rèn)為該語句尚未完成;一個(gè)語句在一行中以do或else結(jié)尾,也會認(rèn)為該語句未完成而繼續(xù)讀取下一行;其他情況下,可以在行末添加“/”來標(biāo)記這一行尚未結(jié)束(建議不要在pattern或字符串以及注釋的中間用反斜杠換行)。
也可以將多個(gè)語句寫到一行中,語句之間用分號“;”隔開。AWK的模式可以是下面的其中一種:
BEGIN END /regular expression/ relational expression pattern && pattern pattern || pattern pattern ? pattern : pattern (pattern) ! pattern pattern1, pattern2BEGIN和END是兩個(gè)比較特殊的模式,他們不對輸入做模式匹配,因?yàn)锽EGIN和END分別是在處理輸入之前和之后。多個(gè)BEGIN塊會被merge成一個(gè)BEGIN塊,這個(gè)BEGIN里面的語句會在開始讀取輸入之前執(zhí)行;多個(gè)END塊會被merge成一個(gè)END塊,這個(gè)END里面的語句會在處理完所有輸入之后執(zhí)行(或者執(zhí)行exit)。注意, BEGIN和END必須是獨(dú)立的,不能糅合在其他pattern表達(dá)式中。并且BEGIN和END不能沒有action部分,也就是說BEGIN和END后面不能沒有花括號{ }。
對于/regular expression/模式,會對所有匹配到該正則表達(dá)式的record執(zhí)行其關(guān)聯(lián)的語句。例如,打印包含“root”的行:awk '/root/ {print $0}' awk_test.txt一個(gè)relationalexpression可以使用后面將要講到的任何運(yùn)算符,這種模式通常用來測試一個(gè)field是否匹配某個(gè)正則表達(dá)式。關(guān)系表達(dá)式是指使用關(guān)系運(yùn)算符(>,>=,<,<=,==,!=)連接一個(gè)或兩個(gè)表達(dá)式組成的式子。
“&&”、“||”和“!”即我們熟知的與、或、非邏輯運(yùn)算符,可以用來將多個(gè)patterns連接在一起,這時(shí)他們是short-circuite valuation(短路求值)的,例如對于&&操作,只要第一個(gè)值是false,那整個(gè)表達(dá)式就是false,就沒必要計(jì)算后續(xù)的值了。圓括號( )可以改變運(yùn)算符的運(yùn)算順序。
例如,打印第一個(gè)field不是“root”的行且第三個(gè)field等于“0.0”的行:awk '$1 != "root" && $3 == "0.0" {print}' awk_test.txt“?:”運(yùn)算符和C語言中的三目的條件運(yùn)算符一樣,pattern1為true則選用pattern2,否則選用pattern3。
pattern1, pattern2形式的表達(dá)式被稱為范圍樣式,它將匹配pattern1的record,匹配pattern2的record以及這兩個(gè)record之間的所有record都匹配出來。正則表達(dá)式
正則表達(dá)式由下表中一系列的字符集合組成。注:其中c表示character,r表示regular expression。
字符 | 含義 |
c | 匹配非元字符c |
/c | 匹配元字符c的字面含義。由于元字符有特殊含義,因此必須加反斜杠來匹配其原始的字符含義。這些字符包括"["、"]"、"/"、"^"、"$"、"."、"|"、"?"、"*"、"+"、"{"、"}"、"("、")"等。 |
. | 匹配一個(gè)字符(包括換行) |
^ | 匹配字符串的開頭 |
$ | 匹配字符串的結(jié)尾(我系統(tǒng)上不支持) |
[abc...] | 匹配字符列表abc…中的任何一個(gè)字符 |
[^abc...] | 匹配字符列表abc…之外的任何一個(gè)字符 |
r1|r2 | 匹配r1或r2 |
r1r2 | 匹配r1且其后緊跟r2 |
r+ | 匹配一個(gè)或多個(gè)r |
r* | 匹配0個(gè)或多個(gè)r |
r? | 匹配0個(gè)或1個(gè)r |
(r) | 分組,匹配圓括號中的所有字符組合r |
r{n} r{n,} r{n,m} | 花括號內(nèi)有1個(gè)或2個(gè)數(shù)值被稱為區(qū)間表達(dá)式:只有一個(gè)數(shù)字n,則表示前面的正則表達(dá)式r重復(fù)n次;有一個(gè)數(shù)字n和一個(gè)逗號,表示r至少重復(fù)n次;有兩個(gè)數(shù)字n和m,表示r重復(fù)n到m次。 區(qū)間表達(dá)式只有在指定--posix或--re-interval的時(shí)候才可用。 |
/y | 匹配一個(gè)單詞開頭和結(jié)尾的空串,例如‘/yballs?/y’匹配‘ball’或‘balls’,但不匹配‘football’ |
/B | 和/y相反 |
/s | 匹配空白字符,同[[:space:]] |
/S | 匹配非空白字符,同[^[:space:]] |
/< | 匹配一個(gè)單詞開頭的空串,例如//<away/匹配‘a(chǎn)way’但不匹配‘stowaway’ |
/> | 匹配一個(gè)單詞結(jié)尾的空串 |
/w | 匹配Word-constituent的字符(包括字母、數(shù)字和下劃線),同‘[[:alnum:]_]’ |
/W | 匹配非word-constituent的字符,同‘[^[:alnum:]_]’ |
/` | 匹配一個(gè)buffer(字符串)開頭的空串,同^ |
/' | 匹配一個(gè)buffer結(jié)尾的空串,同$ |
上表中紅色標(biāo)記的字符用于連接多個(gè)正則表達(dá)式,我們稱之為“正則表達(dá)式運(yùn)算符”或“元字符”。
在字符串常量中有效的轉(zhuǎn)義字符,在正則表達(dá)式中同樣有效。
正則表達(dá)式通常用兩個(gè)斜杠“/ /”包裹起來作為pattern使用,我們稱之為正則表達(dá)式常量。
在中括號表達(dá)式中引用‘/’、‘] ’、‘-’或‘^’,需要加反斜杠,例如“[d/]]”匹配字符'd'或字符']'。
正則表達(dá)式的匹配遵循最左開始最長的匹配的原則,例如下面表達(dá)式的結(jié)果是"<A>bcd":
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
字符類是POSIX標(biāo)準(zhǔn)中引入的特性,一個(gè)字符類表示了某個(gè)特定屬性的字符的集合。POSIX標(biāo)準(zhǔn)定義的字符類包括:字符類 | 含義 |
[:alnum:] | 字母或數(shù)字 |
[:alpha:] | 字母 |
[:blank:] | 空格或tab |
[:cntrl:] | 控制字符 |
[:digit:] | 數(shù)字 |
[:graph:] | 可打印且可見的字符(例如space可打印但不可見,而字符a既可打印又可見) |
[:lower:] | 小寫字母 |
[:print:] | 可打印的字符(非控制字符) |
[:punct:] | 標(biāo)點(diǎn)符號(非字母、數(shù)字、控制字符和空白字符) |
[:space:] | 空白字符(例如空格、tab、換頁符等等) |
[:upper:] | 大寫字母 |
[:xdigit:] | 十六進(jìn)制數(shù)字 |
[root@ubuntu]awk_test:$ awk '/[n[:digit:]]/' regexp.txt我們有時(shí)將匹配字母和數(shù)字寫成/[A-Za-z0-9]/,但在本地化的時(shí)候,一些國家的字母表的字符集并不是這些字符,這時(shí)我們使用/[[:alnum:]]/就可以準(zhǔn)確的匹配而不必?fù)?dān)心本地化后的差異。
還有兩個(gè)特殊的字符序列:Collating Symbols和Equivalence Classes,應(yīng)用在非英語國家的本地化,例如用一個(gè)字符來表示多個(gè)字符/[[.ch.]]/或者定義多個(gè)等價(jià)字符/[[=e=]]/,有興趣的可以自己查閱相關(guān)資料。
注意,/y,/B,/s,/S,/<,/>,/w,/W,/`和/'這些正則表達(dá)式的運(yùn)算符是gawk特有的,gawk的不同選項(xiàng)可能影響如何解析正則表達(dá)式:
無選項(xiàng):上述所有運(yùn)算符均有效(區(qū)間表達(dá)式除外)。
--posix:只支持POSIX標(biāo)準(zhǔn)(包括區(qū)間表達(dá)式)的,那么/w就只表示字面意思的'w'。
--traditional:只支持傳統(tǒng)的UNIX awk的正則表達(dá)式,此時(shí),GNU的運(yùn)算符、區(qū)間表達(dá)式、字符類都不支持,八進(jìn)制和十六進(jìn)制的轉(zhuǎn)義字符("/ddd","/xhex")也會按字面意思解析。
--re-interval:支持區(qū)間表達(dá)式(即使有--traditional選項(xiàng))。
我們可以通過將IGNORECASE設(shè)置為一個(gè)非0的值,使AWK在匹配時(shí)忽略大小寫。也可以用toupper()/tolower()或中括號表達(dá)式,來只對某一些規(guī)則忽略大小寫。動作(actions)
AWK中的動作語句(action statements)都放在花括號{ }中,由大部分語言都有的賦值、條件和循環(huán)語句組成。AWK中的運(yùn)算符、控制語句以及輸入輸出語句都是仿照C語言來定義的。
運(yùn)算符
AWK中運(yùn)算符按照優(yōu)先級由高到低,列舉如下:
運(yùn)算符 | 含義 |
(…) | 分組 |
$ | field引用 |
++ -- | 自增、自減,均可前置和后置 |
^ | 冪運(yùn)算(也可以用**,對應(yīng)的冪運(yùn)算賦值**=) |
+ - ! | 一元運(yùn)算符的+、-、邏輯非 |
* / % | 乘、除、取模 |
+ - | 加、減 |
space | 字符串連接 |
| |& | 管道IO、協(xié)同進(jìn)程的管道IO。getline,print和printf使用 |
< > <= >= != == | 關(guān)系運(yùn)算符 |
~ !~ | 正則表達(dá)式的匹配、不匹配,一般用法是/exp/ ~ /regexp/,其中右側(cè)可以是任何表達(dá)式(不需要必須是正則表達(dá)式常量)。例如$0 ~ /foo/意為查找匹配foo的$0,若匹配,則返回1,不匹配返回0。 |
in | 獲取數(shù)組成員 |
&& | 邏輯與 |
|| | 邏輯或 |
?: | 條件表達(dá)式,格式為expr1 ? expr2 : expr3,如果expr1為true,則表達(dá)式的值為expr2,否則為expr3 |
= += -= *= /= %= ^= | 賦值運(yùn)算符,均支持a=a+b和a+=b這兩種形式。 |
控制語句列舉如下:
if (condition) statement [ elsestatement ] while (condition) statement do statement while (condition) for (initialization; condition; increment) statement for (var in array) statement switch (expression) {case value or regular expression: case-bodydefault: default-body} break continue delete array[index] delete array exit [ expression ] { statements }這些控制語句和ANSI C中類似語句的語法相同。Switch的每個(gè)case分支通常要添加break語句以保證唯一匹配。
輸入輸出語句列舉如下:
I/O statement | 解釋 |
close(file [, how]) | 關(guān)閉文件、管道或協(xié)同進(jìn)程。參數(shù)“how”只有在關(guān)閉協(xié)同進(jìn)程的雙向管道其中一端時(shí)才會用到,是字符串類型,可取"to"或"from"。 |
getline | 把$0賦值為下一個(gè)輸入的record。同時(shí)會更新NF、NR和FNR。 |
getline <file | 把$0賦值為文件file的下一個(gè)record。同時(shí)會更新NF。 |
getline var | 把變量var賦值為下一個(gè)輸入的record。同時(shí)會更新NR和FNR。 |
getline var <file | 把變量var賦值為文件file的下一個(gè)record。 |
command | getline [var] | 執(zhí)行command并將輸出作為getline或getline var的輸入。 |
command |& getline [var] | 執(zhí)行協(xié)同進(jìn)程command并將輸出作為getline或getline var的輸入。(協(xié)同進(jìn)程是gawk的擴(kuò)展,command也可以是一個(gè)socket) |
next | 停止處理當(dāng)前的record,并讀取下一個(gè)record,然后重新從awk程序中第一個(gè)pattern開始處理新的record。如果當(dāng)前已經(jīng)達(dá)到輸入數(shù)據(jù)中最后一個(gè)record,則開始執(zhí)行END{ }程序塊。 |
nextfile | 停止處理當(dāng)前的文件,并從下一個(gè)文件中讀取record,然后重新從awk程序中第一個(gè)pattern開始處理新的record。FILENAME和ARGIND參數(shù)會被更新,F(xiàn)NR被重置為1。如果當(dāng)前已經(jīng)達(dá)到輸入數(shù)據(jù)中最后一個(gè)record,則開始執(zhí)行END{ }程序塊。 |
打印當(dāng)前的record(根據(jù)ORS的值判斷record是否輸出完畢)。 | |
print expr-list | 打印表達(dá)式列表。如果有多個(gè)表達(dá)式,每個(gè)表達(dá)式之間用OFS分隔。(根據(jù)ORS的值判斷record是否輸出完畢) |
print expr-list >file | 打印表達(dá)式列表到文件。如果有多個(gè)表達(dá)式,每個(gè)表達(dá)式之間用OFS分隔。(根據(jù)ORS的值判斷record是否輸出完畢) |
printf fmt, expr-list | 格式化的打印 |
printf fmt, expr-list >file | 格式化的打印到文件。例如: awk 'BEGIN{printf "%#x", 10 >"getnum.txt"}' |
system(cmd-line) | 執(zhí)行命令cmd-line,并將exit status作為返回值。例如: system("uname –a") |
fflush([file]) | 清空已打開的輸出文件或管道文件的所有緩存。如果不帶file參數(shù),則清空標(biāo)準(zhǔn)輸出,如果file參數(shù)為null string,則所有打開的輸出文件和管道的緩存都會被清空。 |
另外,print和printf也允許以下形式的輸出重定向:
print ... >> file | 將輸出追加到文件file |
print ... | command | 向管道寫內(nèi)容 |
print ... |& command | 向協(xié)同進(jìn)程或socket發(fā)送數(shù)據(jù) |
getline命令執(zhí)行成功返回1,到達(dá)文件結(jié)尾返回0,出錯(cuò)返回-1。如果出錯(cuò),則字符串ERRNO包含了出錯(cuò)信息。
注意,如果在打開一個(gè)雙向(two-way)socket的時(shí)候失敗,將會返回一個(gè)非致命的錯(cuò)誤到調(diào)用者。
如果在一個(gè)循環(huán)中使用管道、協(xié)同進(jìn)程或socket(向getline輸出或從print/printf輸入),你必須使用close()來創(chuàng)建新的command或socket的實(shí)例。AWK在管道、協(xié)同進(jìn)程或socket返回EOF的時(shí)候不會自動關(guān)閉它們。AWK里的printf語句和sprintf()函數(shù)支持如下的標(biāo)準(zhǔn)格式轉(zhuǎn)換符:
格式符 | 含義 |
%c | 一個(gè)ASCII字符。如果傳入的參數(shù)是一個(gè)數(shù)值,則將其轉(zhuǎn)換為字符并打印;否則,認(rèn)為傳入的參數(shù)是字符串,只打印該字符串的第一個(gè)字符。 |
%d, %i | 十進(jìn)制數(shù)字(整數(shù)部分) |
%e, %E | 將一個(gè)浮點(diǎn)數(shù)以“[-]d.dddddde[+-]dd”的形式打印。%E使用大寫的E。 |
%f, %F | 將一個(gè)浮點(diǎn)數(shù)以“[-]ddd.dddddd”的形式打印。使用%F(需要系統(tǒng)庫支持),則以大寫字母顯示"not a number"和"infinity"這類的值。 |
%g, %G | 根據(jù)實(shí)際情況轉(zhuǎn)換為%e或%f,選擇其中最簡短的格式。%G對應(yīng)%E。 |
%o | 無符號的八進(jìn)制整數(shù)。 |
%u | 無符號的十進(jìn)制整數(shù)。 |
%s | 字符串。 |
%x, %X | 無符號的十六進(jìn)制整數(shù)。 |
%% | 字符'%'的原意(不會取實(shí)參)。 |
在這些格式符中,'%'和控制字符之間可以放置如下的額外參數(shù)(和C語言中printf的格式符用法相同):
count$ | 位置說明符,指定對第count個(gè)參數(shù)進(jìn)行格式化轉(zhuǎn)換。例如:printf("%2$d/n", 23, 24);打印的值是24。 |
width | 指定格式化后的最短打印長度,如果不夠長則填充空格,默認(rèn)右對齊。 |
- | 左對齊,通常與width搭配使用。 |
0 | 上述不足width的話,使用前導(dǎo)0填充而不是空格。只對數(shù)值類型有效。 |
+ | 對數(shù)值添加正負(fù)號。只對數(shù)值類型有效。 |
# | 以另一種形式打印格式化的內(nèi)容:例如%#o會在數(shù)值前面填0;%#x、%#X會在數(shù)值前面填0x或0X;對%#e、%#E、%#f和%#F,結(jié)果總會有小數(shù)點(diǎn);%#g、%#G總會顯示小數(shù)部分。 |
space | 對正數(shù),前面填一個(gè)空格;對負(fù)數(shù),前面填負(fù)號。 |
.prec | 對%e、%E、%f和%F,指明小數(shù)部分的最大位數(shù);對%g、%G,指明有效數(shù)字的最大長度;對%d、%o、%i、%u、%x和%X,和面前的0width一樣,指明最短打印長度,不足則前面補(bǔ)0;對%s,指明最長打印的字符數(shù)。 |
printf("%1$*2$s/n", "Bye bye!", 12);這個(gè)例子中,1$仍然是位置說明符的作用,而*2$的意思是將第二個(gè)參數(shù)替換在這個(gè)位置,這個(gè)語句就變成了:printf("%1$12s/n", "Bye bye!", 12);特殊文件
在通過print、printf、getline進(jìn)行I/O重定向時(shí),gawk可識別一些特定的文件名,并且支持從gawk的父進(jìn)程(通常是shell)中繼承這些文件的文件描述符來進(jìn)行文件操作。同時(shí),這些文件也可以用作命令行中的數(shù)據(jù)文件名。這些文件為:
/dev/stdin 標(biāo)準(zhǔn)輸入
/dev/stdout 標(biāo)準(zhǔn)輸出
/dev/stderr 標(biāo)準(zhǔn)錯(cuò)誤輸出
/dev/fd/n 與文件描述符n關(guān)聯(lián)的文件
另外,使用破折號“-”也可以引用標(biāo)準(zhǔn)輸入,例如:
some_command | awk -f myprog.awk file1 - file2這條命令中,awk會先讀取file1,然后讀取some_command的輸出,然后讀取file2。
下面三個(gè)特殊的文件名,可以與協(xié)同進(jìn)程操作符“|&”配合使用,創(chuàng)建TCP/ip網(wǎng)絡(luò)連接。
/inet/tcp/lport/rhost/rport 本地端口是lport,與遠(yuǎn)程主機(jī)rhost、遠(yuǎn)程端口rport的TCP/IP連接,如果端口為0,則讓系統(tǒng)自己選擇端口。
/inet/udp/lport/rhost/rport 同上,只是TCP改為UDP。
/inet/raw/lport/rhost/rport 未使用,供后續(xù)擴(kuò)展。
下面這些文件名用來獲取當(dāng)前正在運(yùn)行的gawk進(jìn)程的信息:/dev/pid,/dev/ppid,/dev/pgrpid和/dev/user。目前已被棄用,改為使用PROCINFO獲取進(jìn)程信息。數(shù)值操作函數(shù)
AWK提供以下一些內(nèi)置的算術(shù)函數(shù):
函數(shù) | 作用 |
atan2(y, x) | 求y/x的反正切,單位是弧度。 |
cos(expr) | 求余弦,expr的單位是弧度。 |
exp(expr) | 求指數(shù)。 |
int(expr) | 截?cái)鄀xpr保留整數(shù)部分。 |
log(expr) | 求自然對數(shù)。 |
rand() | 生成一個(gè)隨機(jī)數(shù)N,0 ≤ N < 1。 |
sin(expr) | 求正弦,expr的單位是弧度。 |
sqrt(expr) | 求平方根。 |
srand([expr]) | 為隨機(jī)數(shù)生成器指定一個(gè)新種子expr。如果不指定expr,就使用時(shí)間作為種子。函數(shù)的返回值是之前的種子。 |
Gawk提供以下內(nèi)置的字符串函數(shù):
函數(shù) | 作用 |
asort(s [, d]) | 給數(shù)組s中的成員排序(按照gawk默認(rèn)的升序排序方法),排序完成后,s的數(shù)組下標(biāo)改為從1開始的整數(shù)序列。 如果指定第二個(gè)參數(shù)d,則排序的結(jié)果放在數(shù)組d中,原數(shù)組s不變化。 函數(shù)的返回值是原數(shù)組s的元素個(gè)數(shù)。 |
asorti(s [, d]) | 給數(shù)組s的下標(biāo)排序,(按照gawk默認(rèn)的升序排序方法),排序完成后,s的數(shù)組下標(biāo)改為從1開始的整數(shù)序列,而原來的下標(biāo)改為數(shù)組元素的值。原來的數(shù)組元素的值被丟棄。 如果不想丟棄原來的元素的值,可以指定第二個(gè)參數(shù)d,則排序的結(jié)果放在數(shù)組d中,原數(shù)組s不變化。 函數(shù)的返回值是原數(shù)組s的元素個(gè)數(shù)。 |
gensub(r, s, h [, t]) | 對原始字符串t,將匹配正則表達(dá)式r的子串替換為s。如果字符串h以’g’或’G’開頭,則所有匹配都替換,否則只替換第一個(gè)匹配。函數(shù)的返回值即為執(zhí)行替換后的字符串,也就是說,原始字符串t不會被修改。 如果不指定參數(shù)t,就從當(dāng)前record中讀取,即$0。 在s中,可以通過/1~/9來指代r中第n個(gè)圓括號中的匹配項(xiàng),參考下面的例子1。/0或'&'則表示整個(gè)匹配的內(nèi)容。 |
gsub(r, s [, t]) | 對原始字符串t,將匹配正則表達(dá)式r的子串全部替換為s。函數(shù)的返回值為匹配到的子串的個(gè)數(shù),也就是說,原始字符串t會被修改。 如果不指定參數(shù)t,就從當(dāng)前record中讀取,即$0。 在s中,'&'則表示整個(gè)匹配的內(nèi)容。如果要書寫字符’&'的原意,要寫作"//&"。 |
index(s, t) | 返回字符串t在字符串s中第一次出現(xiàn)的位置。例如index(“abcdefg”, “def”)返回4,即index從1開始。如果沒找到子串t,則返回0。 |
length([s]) | 返回字符串s的長度,如果沒有參數(shù)s,則返回$0的長度。也可以傳入一個(gè)數(shù)組,這時(shí)返回?cái)?shù)組元素的個(gè)數(shù)。 |
match(s, r [, a]) | 在字符串s中匹配正則表達(dá)式r,匹配成功則返回子串的位置(index從1開始),并更新RSTART和RLENGTH,匹配失敗則返回0; 如果有第三個(gè)參數(shù)a,則正則表達(dá)式r中每個(gè)圓括號的匹配內(nèi)容會被依次賦值給數(shù)組a[1]~a[n],而整個(gè)的匹配內(nèi)容則賦值給a[0]。同時(shí),a[m, "start"]和a[m, "length"]兩個(gè)數(shù)組下標(biāo)成員的值為a[m]在s中的位置和字符串長度。 見下面的例子2。 注意,如果在調(diào)用match()之前數(shù)組a不為空,則會先清空a。 |
split(s, a [, r]) | 將字符串s按照正則表達(dá)式r作為分隔符來分割,分割成的每個(gè)field保存在數(shù)組a中,函數(shù)返回fields的數(shù)量。 如果沒有r參數(shù),則根據(jù)FS來分割。 注意,如果在調(diào)用split()之前數(shù)組a不為空,則會先清空a。 |
sprintf(fmt, expr-list) | 根據(jù)格式fmt打印表達(dá)式列表expr-list,最終的字符串作為返回值。例如: sprintf("name%d:%s, name%d:%s", 1, "George", 2, "Tim"); |
strtonum(str) | 將字符串轉(zhuǎn)換為數(shù)值,可識別八進(jìn)制(以0開頭)和十六進(jìn)制(以0x或0X開頭)。例如strtonum(“34”),strtonum(34.50),strtonum(“017”)。 |
sub(r, s [, t]) | 同gsub,但只替換第一個(gè)匹配的子串。 |
substr(s, i [, n]) | 獲取字符串s中,從第i個(gè)字符開始的n個(gè)字符形成的子串,該子串作為返回值。參數(shù)i從1開始。 |
tolower(str) | 將str中的大寫字母都轉(zhuǎn)換為小寫字母后作為函數(shù)返回值。 |
toupper(str) | 將str中的小寫字母都轉(zhuǎn)換為大寫字母后作為函數(shù)返回值。 |
注意,gawk3.1.5支持多字節(jié)的字符,這意味著index()/length()/substr()/match()都是針對字符起作用而不是字節(jié)。
例子1,匹配“me again”,然后將其中的me改為her:[root@ubuntu]awk_test:$ awk 'BEGIN{result=gensub("(me) (again)", "her //2", "g", "tell me again, you go again"); print result}' awk_test.txt tell her again, you go again前面在sed命令中也講到過這種用法,不過這里圓括號不需要加反斜杠轉(zhuǎn)義,并且"/2"要寫成"//2"。
例子2,測試帶第三個(gè)參數(shù)的match函數(shù):[root@ubuntu]awk_test:$ awk 'BEGIN{sstr="name1:George, name2:Tim";pos=match(sstr, "name[1-9]:([a-zA-Z]*), name[1-9]:([a-zA-Z]*)", list); /print pos; print RSTART" "RLENGTH; for (i=0; i<=2; i++) print list[i], list[i, "start"], list[i, "length"];}' awk_test.txt11 23name1:George, name2:Tim, 1 23George 7 6Tim 21 3例子3,split函數(shù)測試:[root@ubuntu]awk_test:$ awk 'BEGIN{sstr="name1:George, name2:Tim, name3:Jason";fcnt=split(sstr, list, "name[[:digit:]]+:"); print fcnt; /for (i=0; i<=fcnt; i++) print list[i];}' awk_test.txt時(shí)間函數(shù)
由于AWK主要的用處就是處理包含時(shí)間戳信息的大型日志文件。因此AWK提供了以下時(shí)間函數(shù)來獲取和轉(zhuǎn)換時(shí)間戳:
mktime(datespec) 將datespec轉(zhuǎn)換為相對于1970-01-01零點(diǎn)的秒數(shù)。參數(shù)datespec的格式為:“YYYY MM DD HH MMSS[ DST]”,其中夏令時(shí)標(biāo)志DST是可選的。例如mktime("2017 2 6 22 54 00")即為2017年2月6日22時(shí)54分0秒,注意這個(gè)時(shí)間是算上時(shí)區(qū)的。舉例來說,在東8區(qū),mktime("19701 1 8 0 1")的返回值為1。
strftime([format [, timestamp[, utc-flag]]]) 將timestamp轉(zhuǎn)換為format指定的格式,timestamp需為相對于1970-01-01零點(diǎn)的秒數(shù)。如果utc-flag不為0或null,則轉(zhuǎn)換結(jié)果是UTC時(shí)間,否則是本地時(shí)間。如果不帶timestamp參數(shù),則使用當(dāng)前時(shí)間;如果不帶timestamp和format參數(shù),則format默認(rèn)與date命令結(jié)果的格式相同,format參數(shù)可用的格式請參考C語言中的strftime()函數(shù),也可參考Effective AWKProgramming[2]中的說明。
systime() 將當(dāng)前時(shí)間轉(zhuǎn)換為相對于1970-01-01零點(diǎn)的秒數(shù)。位操作函數(shù)
Gawk3.1開始的版本,提供了下面的位運(yùn)算函數(shù):
函數(shù) | 作用 |
and(v1, v2) | 按位與 |
compl(val) | 按位取反,同C語言中的'~'運(yùn)算符 |
lshift(val, count) | val左移count位,即val << count |
or(v1, v2) | 按位或 |
rshift(val, count) | val右移count位(高位補(bǔ)符號位),即val >> count |
xor(v1, v2) | 按位異或 |
AWK中可以自定義函數(shù),形式如下:
function name(parameter list) { statements }
或
func name(parameter list) { statements }
其中,name是函數(shù)名,前面添加關(guān)鍵字function或func。圓括號內(nèi)是形參列表,后面大括號內(nèi)書寫函數(shù)的實(shí)現(xiàn)。
例如下面的例子:function add_INT(a, b){ return (a+b);}function add_ARRAY(array){ sum = 0; for(i in array) { sum += array[i]; } return sum;}BEGIN {aint[0]=10; aint[1]=11; aint[2]=12;print add_ARRAY(aint); print add_INT(12, 78);}注意,對于自定義函數(shù),在調(diào)用函數(shù)時(shí),函數(shù)名和左圓括號之間不能有空格(AWK的內(nèi)置函數(shù)沒有這個(gè)限制)。
在AWK中,所有的變量都是全局的,那么就可能出現(xiàn)一個(gè)函數(shù)中的局部變量和主程序的變量重名。如果想避免這種情況,可以在函數(shù)的參數(shù)列表里指明局部變量,方法是將局部變量寫在形參后面,并且用多個(gè)空格與形參列表分開。
例如:function test(a, b, c){ c = 20; sum = a + b;}BEGIN {c = 10;sum = 5;test(11, 22);print c" "sum;}這個(gè)例子中,有c和sum兩個(gè)變量,在函數(shù)test中,會修改c和sum,但是由于指明了c在test()中是作為局部變量的,因此不會影響主程序中c的值。所以print打印出的結(jié)果是“10 33”。
實(shí)際上,參數(shù)列表里的形參都會被認(rèn)為是局部的,用多個(gè)空格分開的做法只是為了代碼的可讀性。
當(dāng)需要將一段通用代碼制作成庫函數(shù)時(shí),自定義函數(shù)就派上了用場,可以配合--source選項(xiàng)來在命令行程序中引用庫函數(shù)。
一些建議:在自定義函數(shù)內(nèi)部,盡量避免定義外部可能使用的變量,例如“i”,“j”這樣的變量,在外部程序中很可能用到,因此在函數(shù)內(nèi)部就不要用這樣的變量命名。建議在函數(shù)內(nèi)變量命名時(shí)以“_”開頭來避免沖突。另外,變量和函數(shù)的命名盡量體現(xiàn)它的作用和含義。最后,如果函數(shù)內(nèi)定義了外部可以使用的全局變量,變量名可以第一個(gè)字母大寫,如“Optind”以和局部變量區(qū)別(不全部大寫是為了防止和AWK內(nèi)置變量混淆)。
可以參考Effective AWK Programming[2]中第10章的部分庫函數(shù)的實(shí)現(xiàn)和第11章的自定義函數(shù)舉例來學(xué)習(xí)自定義函數(shù)的寫法。信號
pgawk可接收SIGUSR1和SIGHUP兩個(gè)信號。SIGUSR1信號會使pgawk生成profile文件(如之前--profile所述),包含自定義函數(shù)的調(diào)用棧,之后pgawk程序繼續(xù)運(yùn)行。SIGHUP信號同樣會產(chǎn)生profile文件,之后pgawk程序退出。
返回值
Gawk執(zhí)行正確返回EXIT_SUCCESS,通常是0;執(zhí)行失敗返回EXIT_FAILURE,通常是1;如果發(fā)生嚴(yán)重錯(cuò)誤,返回2,但有些系統(tǒng)上會返回EXIT_FAILURE。
如果exit語句指定了返回值,則gawk返回這個(gè)指定的值。參考資料
[1] Gawk(1) manpage for GNU Awk 3.1.8
[2] Effective AWK Programming: http://www.gnu.org/software/gawk/manual/gawk.html
新聞熱點(diǎn)
疑難解答
圖片精選