語法變量我覺得awk應該算是屬于動態(tài)編程語言。其變量不需要事先聲明才可以使用。我們想要使用的時候隨時引用即可,不需要事先聲明其數(shù)據(jù)類型。 awk的變量具有三種狀態(tài)。
引用未賦值的變量,其初始值為空字符串或者數(shù)字0。 從gnu awk 4.2.0版本開始提供了typeof()函數(shù)來判斷一個變量的類型。 # awk 'BEGIN{print typeof(a)}'
untyped
# awk 'BEGIN{a;print typeof(a)}'
unassigned
# awk 'BEGIN{a=3;print typeof(a)}'
number
# awk 'BEGIN{a="alongdidi";print typeof(a)}'
string
對未引用且未賦值的數(shù)組進行判斷,返回untyped。但是對未引用的數(shù)組中的未引用的元素進行判斷卻會返回unassigned,這個比較奇怪,需要記住。 # awk 'BEGIN{print typeof(arr)}' untyped # awk 'BEGIN{print typeof(arr["name"])}' unassigned 如果是4.2.0之前的版本,可以使用如下方法判斷變量狀態(tài)。 awk 'BEGIN {
if(a==""&&a==0) {
print "Untyped or unassigned."
} else {
print "Assigned."
}
}'
變量賦值在awk對變量進行賦值可以看作是一個有返回值的表達式(expression)。 # awk 'BEGIN{print a=3}' 3 它等價于: awk 'BEGIN{a=3;print a}' 基于變量賦值可以返回值的特點,可以作連續(xù)的變量賦值。 x=y=z=5
# 等價于
z=5;y=5;x=5
可以將賦值語句放在允許使用表達式來評估值的情況。 # awk 'BEGIN{if(a=1){print "True"}else{print "False"}}'
True
# awk 'BEGIN{if(a=0){print "True"}else{print "False"}}'
False
如果代碼邏輯比較復雜,不建議將變量賦值語句放入表達式。 # awk 'BEGIN{a=1;arr[a+=2]=a=a+6;print arr[9];print arr[3]}' 7 這里不好確定先計算等號(紅色加粗)左邊的數(shù)組索引賦值還是先計算等號右邊的變量賦值,可能會根據(jù)不同的awk版本而不同。 變量使用變量可以在三個位置進行賦值。 awk -v var=val [-v var=val ...] '{code}' var=val file1 var=val file2
不同位置賦值的變量的作用域會有所不同。例如在main代碼塊中賦值的變量在BEGIN中就無法引用,這點根據(jù)awk的工作流程即可得出。 在文件前進行變量賦值,在某些情況下適合于修改FS。 awk '{...}' FS=" " a.txt FS=":" /etc/passwd awk還可以引用shell當中的變量。 # name="alongdidi"
# awk -v nameAwk=$name 'BEGIN{print nameAwk}'
alongdidi
# awk 'BEGIN{print nameAwk='\"$name\"'}'
alongdidi
# awk '{print nameAwk}' nameAwk=$name a.txt
alongdidi
...
數(shù)據(jù)類型awk有兩種基本的數(shù)據(jù)類型:字符串和數(shù)值。從4.2.0開始,還支持正則表達式類型。 數(shù)據(jù)是什么類型不能只看字面意義,例如看到數(shù)字不代表它就是數(shù)值類型。而是要看數(shù)據(jù)所處的上下文:在字符串操作環(huán)境下轉換成字符串類型,在數(shù)值操作環(huán)境下轉換為數(shù)值類型。 轉換分為隱式轉換和顯式轉換。 隱式轉換1、對數(shù)據(jù)進行算術運算可以將其轉換為數(shù)值類型。 對于可轉換成數(shù)值的數(shù)據(jù)會正確轉換成數(shù)值,例如:“123”、“123abc”和“ 123abc”。對于無法正確轉換成數(shù)值的數(shù)據(jù)則轉換成數(shù)值0,例如:“abc123”。 # awk 'BEGIN{a="123";print a+0;print typeof(a+0)}'
123
number
# awk 'BEGIN{a="123abc";print a+0;print typeof(a+0)}'
123
number
# awk 'BEGIN{a=" 123abc";print a+0;print typeof(a+0)}'
123
number
# awk 'BEGIN{a="abc123";print a+0;print typeof(a+0)}'
0
number
算術運算不止有加法,四則運算均可以。"string"+0比較常用,在不改變數(shù)值大小的情況下轉換成數(shù)值。 2、對數(shù)據(jù)進行字符串連接操作可以將其轉換成字符串類型。 使用空格和雙引號連接。 # awk 'BEGIN{print typeof(123"")}'
string
# awk 'BEGIN{print typeof(123 123)}'
string
變量a和b是數(shù)值,使用空格連接后隱式轉換成string,再使用加法運算后隱式轉換成number。 # awk 'BEGIN{a=2;b=3;print a b}'
23
# awk 'BEGIN{a=2;b=3;print (a b)+4}'
27
# awk 'BEGIN{a=2;b=3;print typeof((a b)+4)}'
number
顯式轉換1、使用函數(shù)sprintf()基于預定義變量CONVFMT將數(shù)值轉換成字符串。 CONVFMT的默認值是%.6g。 # awk 'BEGIN{print CONVFMT}' %.6g 變量a是小數(shù),遇到""字符串連接時基于CONVFMT的值隱式轉換成字符串"123.46",然后再由print基于OFMT轉換后輸出。 # awk 'BEGIN{a=123.4567;print a""}'
123.457
# awk 'BEGIN{a=123.4567;CONVFMT="%.2f";print a""}'
123.46
2、使用strtonum()函數(shù)顯式地將字符串轉換成數(shù)字。 # awk 'BEGIN{a="100";print strtonum(a);print typeof(strtonum(a))}'
100
number
# awk 'BEGIN{a="abc";print strtonum(a);print typeof(strtonum(a))}'
0
number
字面量awk中有3種字面量,剛好對應于3種變量的數(shù)據(jù)類型。
字面量的含義就是表示其字面含義,不會再引用其他東西。 數(shù)值字面量整數(shù)、浮點數(shù)、科學計數(shù)法所表示的數(shù)字均為數(shù)值字面量,但是千萬不能使用雙引號包裹,否則就是字符串字面量了。 123 123.00 1.23e+8 1.23e-06 awk內部總是以浮點數(shù)的形式保存數(shù)值。如果在輸出的時候發(fā)現(xiàn)數(shù)值是一個整數(shù),則會自動丟棄小數(shù)部分。 # awk 'BEGIN{a=10.0;print a}' 10 算術運算以下算術運算符的優(yōu)先級由高至低。 ++ -- 自增、自減。
^ ** 冪運算(乘方)。
+ - 一元運算符,表示數(shù)字的正負性。
* / % 乘法、除法和取模。
+ - 二元運算符,加法和減法。
和其他編程語言一樣,自增和自減運算,在變量出現(xiàn)的位置不同時有不同的效果,我們以自增運算為例進行闡述。 a++:先引用a的值參與運算,再對a進行自增。 ++a:先對a進行自增,再引用a的值參與運算。 當自增運算獨立作為語句存在時,二者沒有區(qū)別。 # awk 'BEGIN{a=3;a++;print a}'
4
# awk 'BEGIN{a=3;++a;print a}'
4
當它們參與表達式的時候,情況則不同了。 # awk 'BEGIN{a=3;print a++;print a}'
3
4
# awk 'BEGIN{a=3;print ++a;print a}'
4
4
冪運算。^是冪運算的符號,這個是符合POSIX標準的,而**在有些版本的awk則無法使用,因此推薦僅使用^。 # awk 'BEGIN{print 2^3}' 8 冪運算的運算順序是從右往左,而不是從左往右。因此下面這個示例中的值是512而不是64。 # awk 'BEGIN{print 2^3^2}' 512 賦值操作符的優(yōu)先級是最低的,低于上述的算術運算符。 = += -= *= /= %= ^= **= 不建議書寫一些容易引起歧義的語句。因為不同的awk版本可能有不同的結果。 # awk 'BEGIN{b=3;print b+=b++}' 7 字符串字面量在awk中以雙引號包裹的均為字符串字面量。不能以單引號包裹。 "alongdidi" "29" "\n" " " "" awk中沒有為字符串的連接提供專門的操作符。只需要將字符串緊緊靠在一起或者使用空格(可多個)分隔即可。 # awk 'BEGIN{print "abc""def"}'
abcdef
# awk 'BEGIN{print "abc" "def"}'
abcdef
# awk 'BEGIN{print "abc" "def"}'
abcdef
如果是字符串變量的話,則不能靠在一起,否則它們會組合成另一個變量。 # awk 'BEGIN{a="abc";b="def";print ab}' 字符串的串聯(lián)也具備優(yōu)先級概念,其(空格的)優(yōu)先級低于加減運算。因此如下第一個示例中只有字符串的串聯(lián)(兩個數(shù)值通過空格字符串聯(lián)成字符串),串聯(lián)成功。 # awk 'BEGIN{print 12 " " 23}'
12 23
# awk 'BEGIN{print 12 " " -23}'
12-23
第二個示例,由于減法運算的優(yōu)先級高所以先串聯(lián)" "和-23,識別為二元減法運算等同于0-23等于數(shù)值-23,再將數(shù)值12和數(shù)值-23串聯(lián)(因為中間有空格所以串聯(lián))。所以串聯(lián)的結果是“12-23”而不是“12 -23”。 關于操作符的優(yōu)先級,在下文會有詳述。 正則表達式字面量正則此前我們已經接觸過了。形如這樣的就是正則的字面量。 /Alice/ /A.*e/ /[[:alnum:]]+/ 正則使用在pattern中。匹配方式如下。 "str"~/pattern/ "str"!~/pattern/ 這里的字符串字面量"str"我們也可以使用變量來替代,常使用$0或者$N等。 如果/pattern/單獨出現(xiàn),則等同于$0~/pattern/。 正則匹配結果有返回值,匹配成功返回1,匹配失敗返回0。 因此在使用正則是會有一些需要注意的點。 1、下面兩個是等價的,a的值永遠只會是0或者1,而不會保存正則字面量來用于后續(xù)的引用。下文會詳述如何使用變量來保存正則字面量。 a=/pattern/
a=$0~/pattern/
2、下面三個是逐步等價的,不過一般也不會這么寫(/pattern/~$1)就是了。需要明白這個過程,因為一般awk不會報錯。 /pattern/~$1
$0~/pattern/~$1
0/1~$1
3、期望把正則字面量作為參數(shù)傳遞給函數(shù)。 a=/Alice/
func(arg1,a)
其實傳遞過去的a的值,只會是0或者1而已。 除了這3個以外還會有很多需要注意的點,主要都是源于/pattern/是$0~/pattern/的縮寫以及不能直接將正則字面量賦值給變量來使用。 想要將正則字面量賦值給變量,必須使用4.2.0版本。從該版本開始變量的數(shù)據(jù)類型新增了正則類型。使用方式是在賦值時,在正則字面量前面加一個@。 # awk 'BEGIN{a=@/Alice/;print typeof(a)}' a.txt
regexp
# awk 'BEGIN{a=@/Alice/}$0~a{print}' a.txt
2 Alice female 24 def@gmail.com 18084925203
當使用了正則類型的變量時,就不能簡寫正則匹配了。 a=@/pattern/
$0~a{action} # 正確匹配
a{action} # 錯誤匹配
因此這樣子就無法只打印Alice所在行了。 awk 'BEGIN{a=@/Alice/}a{print}' a.txt gawk支持的正則表達式.:匹配任意單個字符,在gawk中包括了換行符。 ^:匹配行首。 $:匹配行尾。 [...]:匹配中括號內的任意單個字符。 [^...]:匹配中括號外的任意單個字符。 |:邏輯或,二選一。 +:匹配前一個字符至少一次。 *:匹配前一個字符任意次(0次、1次或者多次)。 ?:匹配前一個字符0次或者1次。 ():分組捕獲用于反向引用。 {m}:精確匹配前一個字符m次。 {m,}:匹配前一個字符至少m次。 {m,n}:匹配前一個字符m到n次。 {,n}:匹配前一個字符至多n次。 [:lower:]:小寫字母。 [:upper:]:大寫字母。 [:alpha:]:字母(大小寫均可)。 [:digit:]:數(shù)字。 [:alnum:]:字母或者數(shù)字。 [:xdigit:]:十六進制字符。 [:blank:]:空格或者制表(TAB)字符。 [:space:]:空格字符,包含空格、TAB、換行、回車(carriage return)、進紙(form feed)和垂直(vertical)TAB。 [:punct:]:標點符號字符。非字母、數(shù)字、控制和空格字符。 [:graph:]:既可打印又可見的字符。比如字母是即可打印又可見,但是空格是可打印但是不可見的。 [:print:]:可打印的字符,即非控制字符。 [:cntrl:]:控制字符。 以下是gawk所支持的正則元字符。 \y:單詞的起始或者結束部分的空字符,即單詞的左邊界或者右邊界。\yballs?\y,可以匹配單詞ball或者單詞balls。 \B:單詞內部字符之間的空字符。比如cr\Bea\Bte,可以匹配create不能匹配“crea te”。 \<:單詞的左邊界。 \>:單詞的右邊界。 \s:任意單個空白字符,等同于[[:space:]]。 \S:任意單個非空白字符,等同于[^[:space:]]。 \w:任意單個字母、數(shù)字或者下劃線,等同于[[:alnum:]_],這三種字符也是單詞的組成成分。 \W:等同于\w的取反[^[:alnum:]_]。 \`:絕對行首。例如當遇到“abc\ndef\nghi”,行首和行尾各有3個位置,而絕對行首只會是a的前面,絕對行尾只會是i的后面。 \':絕對行尾。 awk中不支持正則的修飾符,因此若想要忽略大小寫進行匹配,就需要先將其統(tǒng)一轉換成大/小寫再來匹配;或者預設預定義變量IGNORECASE。 # awk '$2~/alice/{print}' a.txt
# awk 'tolower($2)~/alice/{print}' a.txt
2 Alice female 24 def@gmail.com 18084925203
# awk 'BEGIN{IGNORECASE=1}$2~/alice/{print}' a.txt
2 Alice female 24 def@gmail.com 18084925203
awk布爾值在awk中沒有像其他編程語言那樣提供true和false關鍵詞來表示布爾值。不過它的布爾邏輯卻十分簡單:
上面我們說了,正則匹配有返回值,匹配成功返回1,匹配失敗返回0。 布爾運算也有返回值,布爾真返回1,布爾假返回0。 # awk 'BEGIN{if(0){print "True"}}'
# awk 'BEGIN{if(""){print "True"}}'
# awk 'BEGIN{if("0"){print "True"}}'
True
# awk 'BEGIN{if("alongdidi"){print "True"}}'
True
# awk 'BEGIN{if(100){print "True"}}'
True
# awk 'BEGIN{if(a=100){print "True"}}' # 賦值操作有返回值,此處返回數(shù)值100,布爾真。
True
# awk 'BEGIN{if(a==100){print "True"}}'
# awk 'BEGIN{if("alongdidi"~/a+/){print "True"}}' # 正則匹配成功返回1,布爾真。
True
awk中比較操作strnum類型awk中數(shù)據(jù)的來源:
在不考慮gawk 4.2.0版本開始引入的正則表達式類型的情況下,awk基本的數(shù)據(jù)類型為字符串(string)和數(shù)值(number or numeric)。對于來自外部的數(shù)據(jù)(例如從文件中讀取的數(shù)據(jù)),它們理論上應該都是字符串類型。但是有一些字符串類型的數(shù)據(jù)看起來又是數(shù)值的形式,例如a.txt中,從第2行開始的$1、$4和$NF。對于這種類型的數(shù)據(jù)有時候需要將它們視為數(shù)值而有的時候需要將它們視為字符串。 因此POSIX定義了一種叫做“numeric string”的類型來表示此類數(shù)據(jù)。在gawk中使用strnum數(shù)據(jù)類型來表示。當獲取到的用戶數(shù)據(jù)看起來像數(shù)值時,它是strnum類型,在使用的時候將其視為數(shù)值類型。 注意:數(shù)據(jù)類型strnum只針對于awk中除了數(shù)據(jù)常量、字符串常量和表達式計算結果以外的數(shù)據(jù)。例如從文件中讀取的字段、數(shù)組中的元素等等。 雖然來源于管道的外部數(shù)據(jù)原本應該均被識別為string,但是由于某些看起來形似數(shù)值因此在某些情況下被識別為了strnum。 # echo "30" | awk '{print typeof($0)}'
strnum
# echo "+30" | awk '{print typeof($0)}'
strnum
# echo " 30" | awk '{print typeof($0)}'
strnum
# echo " +30" | awk '{print typeof($0)}'
strnum
# echo "30a" | awk '{print typeof($0)}'
string
# echo "a30" | awk '{print typeof($0)}'
string
# echo "30 a" | awk '{print typeof($0),typeof($1)}'
string strnum
大小比較操作比較操作符。 <, >, <=, >=, !=, ==:大小、等值比較。
in:數(shù)組成員測試。
比較規(guī)則。 +----------------------------------------------
| STRING NUMERIC STRNUM
--------+----------------------------------------------
STRING | string string string
NUMERIC | string numeric numeric
STRNUM | string numeric numeric
--------+----------------------------------------------
簡而言之,string的優(yōu)先級最高,一旦操作符兩邊有一方是string,則雙方采用string類型比較。否則其他情況下均采用numeric類型比較。 我們輸出$0和$1以及它們對應的數(shù)據(jù)類型。雖然是外部數(shù)據(jù)理應均為string但是由于形似數(shù)值,因此均被識別為strnum。 # echo ' +3.14' | awk '{print "---"$0"---";print "---"$1"---"}'
--- +3.14---
---+3.14---
# echo ' +3.14' | awk '{print typeof($0);print typeof($1)}'
strnum
strnum
第一組比較:在這組比較中,strnum和string進行比較,只要其中一方是string則雙方采用string方式比較。字符串逐字符進行比較,因此第一對真,后兩對假。 # echo ' +3.14' | awk '{print($0==" +3.14")}'
1
# echo ' +3.14' | awk '{print($0=="+3.14")}'
0
# echo ' +3.14' | awk '{print($0=="3.14")}'
0
第二組比較:在這組比較中,$0和$1均為strnum類型數(shù)據(jù),差別只在一個空格字符,strnum和numeric進行比較按照numeric方式進行比較。因此$0和$1均按照數(shù)值3.14來比較,因此兩對比較返回布爾真。 # echo ' +3.14' | awk '{print($0==3.14)}'
1
# echo ' +3.14' | awk '{print($1==3.14)}'
1
第三組比較:這組和第一組的比較原理其實相同,第一組看懂了,這里就懂了,因此就不做解釋了。 # echo ' +3.14' | awk '{print($1==" +3.14")}'
0
# echo ' +3.14' | awk '{print($1=="+3.14")}'
1
# echo ' +3.14' | awk '{print($1=="3.14")}'
0
第四組比較:awk可以識別echo通過管道傳遞過來的科學計數(shù)表示法的數(shù)值1e2,并將其識別為strnum,然后進行strnum和strnum的比較,按照numeric進行比較,結果顯而易見。對于三目運算符“?:”不懂的,后續(xù)也有解釋。 # echo 1e2 3 | awk '{print $1;print $2;print typeof($1);print typeof($2)}'
1e2
3
strnum
strnum
# echo 1e2 3 | awk '{print ($1>$2)?"True":"False"}'
True
采用字符串比較時需要注意,它是按照(應該吧)ASCII編碼表逐字符一一比較。 圖形1的ASCII編碼(十進制)是49,9是57,a是97。 # 以下均為true。
awk 'BEGIN{print("1"<9)}'
awk 'BEGIN{print(9<"a")}'
awk 'BEGIN{print(1<"a")}'
awk 'BEGIN{print(999<"a11")}'
邏輯運算expr1 && expr2:邏輯與,二元運算符。如果expr1為假則無需計算expr2(即短路運算),直接判定整個邏輯與表達式為假。 expr1 || expr2:邏輯或,二元運算符。如果expr1為真則無需計算expr2(即短路運算),直接判定整個邏輯或表達式為真。 ! expr:邏輯取反(非),一元運算符。 可以使用這個“!”或者“!!”將數(shù)據(jù)轉換成布爾值0或者1。 awk 'BEGIN{print(!99)}' # 0 awk 'BEGIN{print(!"ab")}' # 0 awk 'BEGIN{print(!0)}' # 1 awk 'BEGIN{print(!ab)}' # 1 awk 'BEGIN{print(!"")}' # 1 awk 'BEGIN{print(!!99)}' # 1 awk 'BEGIN{print(!!"ab")}' # 1 awk 'BEGIN{print(!!0)}' # 0 awk 'BEGIN{print(!!ab)}' # 0 awk 'BEGIN{print(!!"")}' # 0 由于變量在初次使用且未賦值的情況下,其值為空字符串或者數(shù)值0,因此其表示的布爾值是假。將這個特性與取反操作相結合,我們可以實現(xiàn)對指定范圍的數(shù)據(jù)進行處理。思路是:
例如,打印a.txt的第1行至第4行數(shù)據(jù)。我們可以使用以往使用的方式實現(xiàn)。 awk 'NR<=4{print}' a.txt
awk 'NR>=1&&NR<=4{print}' a.txt
或者使用我們剛說的思路實現(xiàn)。 # awk 'NR==1{print;along=!along;next}along{print}NR==4{along=!along;next}' a.txt
ID name gender age email phone
1 Bob male 28 abc@qq.com 18023394012
2 Alice female 24 def@gmail.com 18084925203
3 Tony male 21 aaa@163.com 17048792503
也可以使用字符串連接存儲到變量中后,在最后(END)再打印。 awk 'NR==1{print;along=!along;next}along{multiLine=multiLine$0"\n"}NR==4{along=!along;next}END{printf multiLine}' a.txt 運算符優(yōu)先級運算/操作符(operator)優(yōu)先級由高至低排列。 () $ ++ -- + - ! * / % + - space | |& < > <= >= != == ~ !~ in && || ?: = += -= *= /= %= ^= ()優(yōu)先級最高;一元運算符的優(yōu)先級高于大多數(shù)二元運算符;space是空格,表示字符串連接,在字符串字面量的字符串串聯(lián)中我們已經解釋過字符串串聯(lián)優(yōu)先級對于串聯(lián)結果的影響了。 對于優(yōu)先級相同的運算符,運算順序是從左往右,不過對于賦值和冪運算來說是從右往左運算。 a-b+c ==> (a-b)+c a=b=c ==> a=(b=c) 2**2**3 ==> 2**(2**3) >除了是大于號,也表示輸出重定向,一般來說它的運算符應保持最低才不會出現(xiàn)意外的結果。 awk 'BEGIN{print "foo">1<2?"true.txt":"false.txt"}' awk 'BEGIN{print "foo">(1<2?"true.txt":"false.txt")}' 流程控制語句這部分其實大多數(shù)的編程語言都是一樣的,我的博客中的bash的學習筆記對于這些基本的東西也描述較多,因此對于重復的東西就不再贅述了。 在awk中,代碼塊{...}并不會分隔變量的作用域。例如在某個循環(huán)中我們已經使用了一個變量i,那么退出循環(huán)以后,這個變量i仍然有效。 # awk 'BEGIN{for(i=1;i<=10;i++){} print i}' 11 if語句# 單分支 if(cond){ statements } # 雙分支 if(cond){ statements1 }else{ statements2 } # 多分支 if(cond1){ statements1 }else if(cond2){ statements2 }else if(cond3){ statements3 }else{ statementsLast } 我們來看一個有趣的示例。有一對夫妻,丈夫是一名程序員,妻子對丈夫說“出去買一個包子,如果看到賣西瓜的,就買兩個?!?/p> # 自然語言理解 買1個包子 if(看到賣西瓜的){ 買2個西瓜 } # 編程語言理解 if(看到賣西瓜的){ 買2個包子 }else{ 買1個包子 } 示例。 # cat if.awk BEGIN{ if(mark>=0&&mark<60) { print "bad" } else if(mark>=60&&mark<90) { print "ordinary" } else if(mark>=90&&mark<=100) { print "Good" } else { print "error mark" } } # awk -v mark=100 -f if.awk Good 條件表達式條件表達式(Conditional Expression),也就是我們常見的三目運算符。 selector ? if-true-exp : if-false-exp 三者均是表達式,首先計算selector,若值為真(我們已經在講解布爾值時解釋過awk的真和假的概念了)則計算表達式if-true-exp,并將其返回值作為整個表達式的返回值,否則計算if-false-exp并將其值作為整個表達式的返回值。 來看兩個成功的示例。 awk 'BEGIN{mark=60;grade=mark>=60?"pass":"fail";print grade}' awk 'BEGIN{mark=60;mark>=60?grade="pass":grade="fail";print grade}' 要注意這三者均是表達式,它們和普通語句的不同點在于它們可以計算評估并擁有返回值,而普通的語句是不行的。例如。 # awk 'BEGIN{mark=60;mark>=60?print "pass":print "fail"}' awk: cmd. line:1: BEGIN{mark=60;mark>=60?print "pass":print "fail"} awk: cmd. line:1: ^ syntax error switch語句awk的switch語句的功能和bash中的是一樣的。區(qū)別在于awk的switch語句的每個分支需要顯式使用break才可離開分支,否則會發(fā)生分支的穿透。 switch(expression) { case val1|regex1 : statements1 case val2|regex2 : statements2 case val3|regex3 : statements3 ... ... [default: statemtsLast] } 示例如下。除了每個分支的break以外,其余和我們在bash中所見到的switch并無二致。 # cat switch1.awk { switch($0){ case 1: print "Monday." break case 2: print "Tuesday." break case 3: print "Wednesday." break case 4: print "Thursday." break case 5: print "Friday." break case 6: print "Saturday." break case 7: print "Sunday." break default: print "What day is today?" } } # awk -f switch1.awk 0 What day is today? 8 What day is today? 1 Monday. 2 Tuesday. What day is today? # Ctrl+d結束輸入 我們可以注釋掉case=1|2|3|4分支的break指令來查看效果就可以理解什么是分支穿透,這個很簡單就不演示了。 如果我們期望根據(jù)用戶的輸入來輸出工作日或者周末信息,可以注釋掉break。 { switch($0){ case 1: case 2: case 3: case 4: case 5: print "Weekday." break case 6: case 7: print "Weekend." break default: print "What day of today?" break } } case的值不支持邏輯或,會報錯。 # cat switch4.awk { switch($0){ case 1|2|3|4|5: print "Weekday." break case 6|7: print "Weekend." break default: print "What day of today?" break } } # awk -f switch4.awk awk: switch4.awk:3: case 1|2|3|4|5: awk: switch4.awk:3: ^ syntax error 像這種情況就只能用正則了。 { switch($0){ case /[12345]/: print "Weekday." break case /[67]/: print "Weekend." break default: print "What day is today?" break } } 循環(huán)while循環(huán)。 while(condition) { statements } do while循環(huán)。 do { statements } while(condition) for循環(huán)。 for(expr1;expr2;expr3) { statements } for循環(huán)遍歷數(shù)組。 for(idx in arrary) { statements } break和continueawk中的break和continue與bash中的幾乎相同,區(qū)別僅在于awk中的break還用于switch...case...語句中的退出分支。 # awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){break}print i}}' 1 2 # awk 'BEGIN{for(i=1;i<=5;i++){if(i==3){continue}print i}}' 1 2 4 5 next和nextfile講到next就會和getline一同解釋。我們直接看命令執(zhí)行結果。 # seq 1 5 | awk 'NR==3{next}{print}' 1 2 4 5 # seq 1 5 | awk 'NR==3{getline}{print}' 1 2 4 5 命令的執(zhí)行結果相同,但是執(zhí)行的過程是不同的。 代碼塊中的pattern和{action}一同構成一條規(guī)則(rule),在這個示例中有2條規(guī)則,第一條規(guī)則是pattern為NR==3的,第二條規(guī)則則沒有pattern,意味著每一條記錄都符合該規(guī)則。 next:讀取下一行,然后重新回到規(guī)則的頭部(NR==3的位置)處理。 getline:讀取下一行,然后在當前位置(getline的位置)繼續(xù)往下處理。 也就是說第一條命令next之后重新回到規(guī)則頭部判斷NR是否等于3了,而第二條命令在getline之后執(zhí)行的是無pattern的print。 nextfile:next命令是停止當前正在處理的記錄而后進入下一條記錄,而nextfile命令則是停止當前正在處理的文件而后進入下一個文件的處理。 # awk 'NR==3{nextfile}{print}' a.txt a.txt ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 ID name gender age email phone ... ... 10 Bruce female 27 bcbd@139.com 13942943905 # awk 'FNR==3{nextfile}{print}' a.txt a.txt ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 ID name gender age email phone 1 Bob male 28 abc@qq.com 18023394012 exitexit [code] exit用于退出awk程序,可以帶返回值。 如果在BEGIN或者main代碼塊中執(zhí)行exit,則會停止當前處理而后執(zhí)行END代碼塊(若有的話)的內容。也就是說exit命令的執(zhí)行包含了執(zhí)行END代碼塊。 如果在END代碼快中執(zhí)行exit,則程序直接退出。 我們在講解BEGIN和END代碼塊時曾說過,若存在END代碼塊但是沒有遇到EOF(遇到文件的結尾或者STDIN中鍵入Ctrl+d),則END代碼塊阻塞不執(zhí)行。但是現(xiàn)在有exit的執(zhí)行,即使沒有EOF我們也可以執(zhí)行END代碼塊的內容了。 # awk 'BEGIN{print "Hello world!";exit}END{print "This is not end!!!"}' # 這里既沒有文件的處理,下面2行也不是STDIN。 Hello world! This is not end!!! 如果沒有BEGIN,則至少需要在main中做一次輸入。 # awk '{print "Hello world!";exit}END{print "This is not end!!!"}' 1 # 用戶輸入 Hello world! This is not end!!! 有時候為了讓BEGIN或者main中的exit真的像其他編程語言(如bash)那樣直接退出程序,我們可以在exit前設置一個變量(如flag等),然后在END的頭部判斷該變量來決定是否退出。 # awk 'BEGIN{print "Hello world!";flag=1;exit}{}END{if(flag){exit};print "end code"}' Hello world! 較完整的偽代碼如下: BEGIN { ... ... if(begin cond) { flag=1 exit } ... } { ... ... if(main cond) { flag=1 exit } ... ... } END { if(flag){exit} # 必須在END頭部。 ... ... } exit可以指定退出狀態(tài)碼(返回值),如果帶狀態(tài)碼的exit只有一次,那么采用該狀態(tài)碼。 # awk 'BEGIN{exit 100}' # echo $? 100 # awk '{exit 100}' # 這里必須至少一次輸入,如果直接Ctrl+d,返回的是0而不是exit的狀態(tài)碼。 1 # echo $? 100 # awk '{exit 100}' # 直接Ctrl+d的結果。 # echo $? 0 # awk 'END{exit 100}' # 這里也是直接Ctrl+d,結果不同是因為代碼塊不同。 # echo $? 100 如果有多個exit并且不止一個exit有狀態(tài)碼的話,則退出狀態(tài)碼是最后一次有執(zhí)行的的帶狀態(tài)碼的exit的狀態(tài)碼。 # awk 'BEGIN{exit 10}{exit 20}END{exit 30}' # echo $? 30 # awk '{exit 20}END{exit 30}' # echo $? 30 # awk '{exit 20}END{exit 30}' 1 # echo $? 30 # awk 'BEGIN{exit 10}{exit 20}END{exit}' # echo $? 10 # 最后的exit沒有狀態(tài)碼,最后一次有狀態(tài)碼的exit是BEGIN中的,main中的不會執(zhí)行。
|
|