awk用法小結
原文網址:
http://www.cnblogs.com/276815076/archive/2011/12/05/2276625.html
awk概述
為什麼使用awk: awk 是一種程式語言。 它具有一般程式語言常見的功能。 因awk語言具有某些特點, 如:使用直譯器(Interpreter)不需先行編譯; 變數無型別之分(Typeless), 可使用文字當陣列的下標 (Associative Array)。。。等特色。 因此,使用awk撰寫程式比起使用其它語言更簡潔便利且節省時間。awk還具有一些內建功能,使得awk擅於處理具資料行(Record), 欄位(Field)型態的資料; 此外, awk 內建有管道(pipe) 的功能,可將處理中的資料傳送給外部的Shell命令加以處理,
再將Shell命令處理後的資料傳回awk程式, 這個特點也使得awk程式很容易使用系統資源。由於awk具有上述特色, 在問題處理的過程中,可輕易使用awk來撰寫一些小工具;這些小工具並非用來解整個大問題,它們只扮演解個別問題過程的某些角色, 可藉由Shell所提供的管道(pipe)將資料按需要傳送給不同的小工具進行處理,以解決整個大的問題。 這種解決方式, 使得這些小工具可因不同需求而被重複組合及重用(reuse); 也可藉此方式來先行測試大程式原型的可行性與正確性,將來若需要較高的執行速度時再用C語言來改寫。這是awk最常被應用之處。
若能常常如此處理問題, 讀者可以以更高的角度來思考抽象的問題,不會被拘泥於細節的部份。
如何取得awk: 一般的LINUX/UNIX作業系統, 都預設安裝了awk。 只不過awk版本可能不太相同。
awk如何工作: 為方便解釋awk程式的框架結構和有關術語(terminology), 先以一個員工薪工資檔案(emp.dat ), 進行介紹,檔案內容如下:
A101 chenying 100 210
A304 linxiyu 110 215
S283 degnming 130 209
S267 liuchao 125 220
B108 hejing 95 210
檔案中各欄位依次為:員工ID, 姓名, 薪資率,及實際工時。 ID中的第一碼為部門名稱程式碼,。 “A”,”B”,“S”分別表示”“accp部門”,“benet部門”,“市場部”。
這裡主要說明awk程式的主要架構及工作原理, 並對一些重要的名詞進行必要的解釋。 並體會一下awk語言的主要精神和awk與其它語程式言的不同之處。 首先了解一下幾個名詞定義:
名詞定義1 資料行: awk從資料檔案上讀取資料的基本單位,awk在進行資料處理的時候是以行為單位的,grep也是以行為單位進行資料處理的。。以emp.dat為例, awk讀入的第一個資料行是第一行 “A125 Jenny 100 210″ ,第二個資料行是第二行 “A341 Dan 110 215″ ,其他的依次類推,
名詞定義2: 欄位(Field) : 為資料行上被分隔開的子字串。 以資料行”A125 Jenny 100 210″為例, 第一欄 第二欄 第三欄 第四欄的內容分別是: “A125″ ,”Jenny”, 100, 210 ,預設情況下,awk以空格分隔各個欄位。
在linux/UNIX 的命令列上輸入一下格式的指令: ( “$”表Shell命令列上 的提示符號)
$awk ‘awk程式’ 資料檔名
上面這條語句中,awk會先編譯該程式, 然後執行該程式來處理所指定的資料檔案。
awk程式的主要結構: awk程式中主要語法是 Pattern { Actions},即模式{動作}, 所以常見的awk 程式的機構如下:
Pattern1 { Actions1 }
Pattern2 { Actions2 }
……
Pattern3 { Actions3 }
Pattern 是什麼? awk 可接受許多不同型態的Pattern。 一般常使用 “關係表示式”(Relational expression) 來當成 Pattern。 例如: x > 34 是一個Pattern, 判斷變x與34是否存在大於的關係。 x == y是一個Pattern, 判斷變數x與變數y是否存在等於的關係。 上式中 x>34 ,x ==y 便是典型的Pattern。 awk 提供C 語言中常見的關係運算符(Relational Operators) 如 >, <, >=, <=,
==, != 。此外, awk 還提供~ (匹配match) 及 !~(非匹配not match) 二個關係運算符。 其用法與涵義如下:
如果A 為一字串, B 為一正則表示式(Regular Expression) ,A ~ B 判斷字串A 中是否包含能匹配(match)B表示式的子字串。 A !~ B 判斷字串A 中是否不包含能匹配(match)B表示式的子字串。 例如:”banana” ~ /an/ 整個是一個Pattern。 因為”banana”中含有可以匹配/an/的子字串,所以此關係式成立 (true),整個Pattern的值為true。
有少數awk文章, 把~, !~ 當成另一類的操作符(Operator),而不作為一種 Relational Operator。在這裡將這兩個運算子當成一種Relational Operator。
Actions 是什麼?
Actions 是由許多awk指令構成。awk 的指令與C 語言中的指令十分類似。 例如:awk 的I/O指令: print, printf( ), getline……
awk的流程控制指令:
if(…){…} else{…}, while(…){…}…
awk 如何處理模式和動作的呢?(Pattern { Actions } ? ),awk 會先判斷(Evaluate)Pattern的值, 如果Pattern的值為true (或不為0的數字,或不是空的字串),則awk將執行該Pattern所對應的Actions。否則,如果Pattern之值不為true, 則awk 將不執行該Pattern所對應的Actions。
例如:如果awk程式中有下列兩指令 50 > 23 {print “Hello! The word!!” } “banana” ~ /123/ { print “Good morning !” }
awk會先判斷50 >23 是否成立,因為該式成立,所以awk將打印出”Hello! The word!!”。 而另一Pattern 為”banana” ~/123/, 因為”banana” 內未含有任何子字串可匹配(match)/123/,所以Pattern 的值是false,awk將不會打印出 “Good morning !”
awk 如何處理{ Actions } 的語法是什麼呢?
有時語法Pattern { Actions }中,Pattern 部分會省略,只剩下 {Actions}。這種情況表示”無條件執行這個Actions”。
awk 的欄位變數
awk 所內建的欄位變數及其涵意如下:
欄位變數 含義
$0 一字串, 其內容為目前awk 所讀入的資料行。
$1 $0 上第一個欄位的資料。
$2 $0 上第二個欄位的資料
其餘類推 。
讀入資料行時,awk如何更新(update)這些內建的欄位變數?
當awk 從資料檔案中讀取一個數據行時,awk 會使用內建變數$0 進行記錄。當$0 被改動時(例如:讀入新的資料行或改變$0的值…), awk 會立刻重新分析$0的欄位情況,並將$0上各欄位的資料用$1,$2…進行記錄。
awk 的內建變數(Built-in Variables)
awk 提供了許多內建變數,使用者可以在程式中使用這些變數來取得相關資訊。常見的內建變數有:
內建變數 含義
NF Number of Fields 為一整數,其值表示$0上所存在的欄位數目。
NR Number of Records為一整數,其值表示awk 已讀入的資料行數目。
FILENAME awk正在處理的資料檔案檔名。
例如:awk 從資料檔案emp.dat 中讀入第一筆資料行“A101 chenying 100 210”之後,程式中:$0 的值就是“A101 chenying 100 210”,$1的值是 “A101″,$2 的值是 “chenying”,$3的值是100,$4的值是210,$NF的值是4,$NR 的值是1,$FILENAME 的值是”emp.dat”。
awk 的工作流程:執行awk 時,它會反覆進行下列四步驟:
1、自動從指定的資料檔案中讀取一個數據行。
2、自動更新(Update)相關的內建變數之值。 如:NF,NR,$0…
3、依次執行程式中所有的Pattern { Actions } 指令。
4、當執行完程式中所有Pattern { Actions } 時,如果資料檔案中還有未讀取的資料,則反覆執行步驟1到步驟4。awk會自動重複進行上述4個步驟,使用者不須於程式中編寫這個迴圈(Loop)。
列印檔案中指定的欄位資料並加以計算
awk 處理資料時,它會自動從資料檔案中一次讀取一行記錄,並將該資料切分成一個個的欄位;程式中可使用$1,$2,…直接取得各個欄位的內容。這個特色讓使用者輕易地使用awk 編寫reformatter 來改變資料格式。
例子:以檔案emp.dat 為例,計算每人應發工資並列印報表。
分析:awk 會自行一次讀入一行資料,所以程式中僅需告訴awk 如何處理所讀入的資料行。執行如下命令:
$ awk ‘{ print $2,$3 * $4 }’ emp.dat
執行結果時,螢幕出現 :
chenying 21000
linxiyu 23650
…………….
linux/UNIX 命令列上,執行awk 的語法為:$awk ’awk程式’ 欲處理的資料檔案檔名;本例中程式部分是{print $2, $3 * $4}。 把程式置於命令列時,程式之前後須以’ 括住。emp.dat 為指定給該程式處理的資料檔案檔名。本程式中使用:Pattern { Actions } 語法,但是Pattern 部分被省略,表無任何限制條件,所以awk讀入每筆資料行後都將無條件執行這個Actions。 print為awk所提供的輸出指令,會將資料輸出到標準輸出stdout(螢幕)。
print 的引數間彼此以”,” (逗號) 隔開,打印出資料時彼此間會以空白隔開。
如果將上述的程式部分儲存於檔案pay1.awk 中,執行命令時再指定awk 程式檔案的檔名。 這是執行awk 的另一種方式,適用於程式較大的情況, 其語法如下:
$ awk -f awk程式檔名 資料檔案檔名
$ awk -f pay1.awk emp.dat
awk 中也提供與 C 語言中類似用法的printf()函式,使用該函式可進一步控制資料的輸出格式。編輯另一個awk程式如下,並取名為pay2.awk
{ printf(“%10s Work hours: %3d Pay: %5d/n”, $2,$3, $3* $4) }
執行下列命令
$awk -f pay2.awk emp.dat
執行結果螢幕出現:
chenying Workhours: 100 and pay: 21000
linxiyu Workhours: 110 and pay: 23650
degnming Workhours: 130 and pay: 27170
liuchao Workhours: 125 and pay: 27500
hejing Workhours: 95 and pay: 19950
在awk使用方法中,Pattern{ Action }是awk使用的最主要語法。如果Pattern的值為真則執行它後方的Action。awk中常使用”關係表示式” (Relational Expression)當做Pattern。
awk 中除了>,<,==,!= 等關係運算符(Relational Operators)外,另外提供 ~(匹配match),!~(不匹配Not Match)二個關係運算符。利用這兩個運算子,可判斷某字串是否包含能匹配所指定正則表示式的子字串。 有這麼多關係運算符,使用awk可以輕易的 進行字串的比較,並編寫字串處理的程式。
例如,單位的職工如下所示:
[[email protected] pub]# cat emp.dat
A101 chenying 100 210
A304 linxiyu 110 215
S283 degnming 130 209
S267 liuchao 125 220
B108 hejing 95 210
其中A開頭的表示accp部門工作人員,工資加薪5%,加薪後員工薪資仍低於110的,以110計。編寫awk程式列印新的員工薪資率報表。
[分析] :這個程式須先判斷所讀入的資料行是否合於指定條件,再進行某些動作。awk中Pattern { Actions } 的語法已涵蓋這種 “if (條件) {動作} “的架構。編寫awk指令碼程式adjust1.awk,指令碼內容如下:
[[email protected] pub]# cat adjust1.awk
#!/bin/awk -f
{
if($0~/A/){$3*=1.05}
if($3<110){$3=110}
printf(“%s %-8s %d/n”,$1,$2,$3)
}
接下來,執行程式指令碼:
[[email protected] pub]# awk -f adjust1.awk emp.dat
結果如下:
[[email protected] pub]# awk -f adjust1.awk emp.dat
A101 chenying 110
A304 linxiyu 115
S283 degnming 130
S267 liuchao 125
B108 hejing 110
通過以上輸出,可以發現,數值改變的只有在工號中有A的行。下面來看一下awk執行的步驟:首先,awk從資料檔案中每次讀入一個數據行, 依序執行程式中所有的 Pattern{ Action }指令:
if($0~/A/){$3*=1.05}
if($3<110){$3=110}
printf(“%s %-8s %d/n”,$1,$2,$3)
再從資料檔案中讀取下一行記錄繼續進行處理。
第一個Pattern { Action }是: if($0~/A/){$3*=1.05},if($0~/A/)是一個Pattern,用來判斷該筆資料行的第一行是否包含”A”。 其中~/A/是一個正則表示式Regular Expression,用來表示在整行資料中含有A的字串。Actions部分為{$3*=1.05},$3*=1.05與$3=$3*1.05意義相 同。 運算子”*=” 的用法與C語言中的用法一樣。此後與C語言中用法相同的運運算元或語法將不予贅述。
第二個 Pattern { Actions } 是:if($3<110){$3=110},如果第三欄的資料內容(表薪資率)小於110, 則調整為110。
第三個 Pattern { Actions } 是:printf(“%s %-8s %d/n”,$1,$2,$3),省略了Pattern(無條件執行Actions), 故所有資料行調整後的資料都將被印出。
awk程式中允許使用字串當做陣列的下標(index),這個特點有助於資料的統計。(使用字串當下標的陣列稱為Associative Array)
首先建立一個名為kecheng.dat資料檔案,內容是學生選課的內容;第一欄為學生姓名,其後為該生所學課程,內容如下:
[[email protected] pub]# cat kecheng.dat
zhangsan math english chinese
lisi computer chinese english
wangwu dianzi chinese math
zhaoliu huanjing english chinese
awk中陣列不需要宣告,也不用指定陣列的大小,直接使用字串當陣列的下標(index)。 以上邊學生選課為資料檔案統計一下kecheng.dat 中學習各門課程的人數。
這種情況,有二項資訊必須儲存: (a) 課程名稱, 如:math,English,共有哪些課程事先並不明確。 (b)各課程的選修人數。 如: 有幾個人選修了“math”。
在awk 中只用一個數組就可同時記錄上述資訊。 方法如下:使用陣列Number[ ]:以課程名稱當Number[ ]的下標。 以Number[ ] 中不同下標所對映的元素代表各門課程的人數。 例如:有2個學生學習“math”,則以Number["math"]=2表示。如果學習math的人數增加一人,則 Number[“math”]=Number[“math”]+1 或Number[“math”]++ 將學習該門課程的人數加1。
那麼如何讀出陣列中儲存的資訊呢?以math為例,宣告int Arr[100]陣列後,如果想顯示陣列中的數值,使用一個for迴圈就可以了,如:for(i=0; i<100; i++) printf(“%d/n”, Arr[i]); 即可。上式中:陣列Arr[ ] 的下標 :0,1,2,…,99, 陣列Arr[ ] 中各下標所對應的值:Arr[0],Arr[1],…,Arr[99]。
上面說了awk 中使用陣列不須宣告,以陣列number[]為例,程式執行前,並不知有哪些課程名稱可能被當成Number[ ]的下標。在awk中提供了一個指令,通過該指令awk會自動找尋陣列中所有使用過的下標。以Number[ ] 為例,awk將會找到“math”,“english”,使用該指令時,只要首先指定要找尋的陣列和變數即可,awk會記錄從陣列中找到的每一個下標。
例如:for(course in Number){…}
指定使用course 來記錄awk 從Number[ ]中找到的下標,awk每找到一個下標,就用course記錄該下標的值且執行{…}中的指令。
例子:統計kecheng.dat中每門課程有多少學生學習,並輸出結果。
處理方法:建立使用awk編寫的指令碼程式,指令碼的內容如下:
[[email protected] pub]# cat course.awk
#!/bin/awk -f
BEGIN{
FS=” “}
{
for(i=2;i<=NF;i++){number[$i]++;}
}
END{
for(course in number)printf(“%10s %d/n”,course,number[course]);
}
在程式中需要注意,awk程式主要有三部分組成,BEGIN,中央處理部分和END三個部分,BEGIN和END是awk的保留字,後面必須是“{”,如果沒有緊跟著“{”,執行程式的時候,會報錯;FS是field separate的縮寫,用來儲存域分隔符。
執行結果如下:
[[email protected] pub]# awk -f course.awk kecheng.dat
computer 1
english 3
dianzi 1
chinese 4
math 2
huanjing 1
程式解釋:這程式包含三個Pattern { Actions }指令。
第一個Pattern { Actions }指令中的FS上面已經說過了,後面跟著分隔資料檔案的分隔符,在這裡,是以空格來進行分隔資料行的沒一個部分的,所以FS=” “;如果是分隔/etc/passwd這個檔案,那麼FS=”:”,在以後分隔資料檔案的時候,一定要選擇正確的域分隔符,並用FS進行設定。
第二個Pattern { Actions }指令中省略了Pattern 部分,所以每行資料讀入後,都會在Actions部分將逐次無條件執行。awk在讀入第一行資料zhangsan math english chinese的時候,因為該行資料有NF=4個欄位,所以Action 的for迴圈中i從2開始,因為第一個欄位是學生的名字,不用進行統計;而其後的各個域需要統計,所以for迴圈中i取值為2,3,4。 Number[$i]++ 這句中,在i=2時,$2是第二個域的值,即$2=math,Number[math]的值從預設的0,++變成了1
; i=3時$i=english,Number[english]的值預設是0,++變成了1 ; 同理,i=4時 $i=chinese,Number[chinese]的值從預設的0,++後變成了1 ; 第一行資料處理完後,再次讀入下一行,根據$i的內容,如果陣列中沒有,就會新新增一個課程,並將選擇給課程的學生數加1,如果該課程在陣列中已經存在, 只將改課程的人數加1。
第三個Pattern{Actions}指令中END 為awk的保留字,而且必須是大寫,是Pattern的一種。END 成立(其值為true)的條件是: “awk處理完所有資料,即將離開程式時”,平常讀入資料行時,END並不成立,所以END後面的Actions 並不被執行;只有當awk讀完所有資料時,Actions才會被執行。
BEGIN與END 有點類似,是awk 中另一個保留的Pattern。 唯一不同的是:“以BEGIN 為Pattern的Actions 在程式一開始的時候被執行一次”, NF 為awk 的內建變數,用來表示awk正在處理的資料行中所包含的欄位的個數。
awk程式中以$開頭的變數, 都是下面這種意思:以i= 2 為例,$i=$2 表示第二個欄位資料(實際上,$在awk 中 為一運算子(Operator),用以取得欄位資料)。
awk程式中允許使用Shell指令,使用管道在awk和系統中進行資料傳遞,所以awk可以很容易的使用系統資源。
比如寫一個awk程式來打印出當前系統上有多少使用者登入。awk的指令碼檔名為usernumber.awk,指令碼內容如下:
[[email protected] pub]# cat usernumber.awk
#!/bin/awk -f
BEGIN{
while(“who”|getline) n++;
print n;
}
執行結果如下:
[[email protected] pub]# awk -f usernumber.awk
2 #即有兩個使用者登入了系統
解釋:awk 程式並不一定要處理資料檔案,上例中沒有輸入任何資料檔案。BEGIN 和END是awk 中的一種Pattern。以BEGIN 為Pattern的Actions,只有在awk開始執行程式,尚未輸入任何檔案前,執行一次且僅被執行一次) ,“|”和在shell中一樣,在awk 中也表示管道。awk把|之前的字串“who”當成Shell的命令,並將該命令送往Shell執行,執行的結果通過管道輸出到awk程式中。
getline為awk所提供的輸入指令。getline 一次讀取一行資料,若讀取成功則return 1,若讀取失敗則return -1,若遇到檔案結束(EOF),則return 0;
getline的用法和例子:
當getline左右沒有重定向符|或<時,getline讀去當前檔案的第一行並將資料儲存到變數中,如果沒有變數,則資料儲存到$0中;由於awk在處理getline之前已經讀入了一行,所以getline得到的返回結果是隔行的。
當getline左右有重定向符|或<時,getline作用於定向輸入檔案,由於該檔案是剛開啟,awk並沒有讀入一行資料,而getline讀入了一行資料,那麼getline返回的是該檔案的第一行,而不是隔行。
[email protected] pub]# awk ‘BEGIN{“cat kecheng.dat”|getline var;print var;}’
[[email protected] pub]# awk ‘BEGIN{“cat kecheng.dat”|getline;print $0;}’
[[email protected] pub]# awk ‘BEGIN{getline var<”kecheng.dat”;print var;}’
[[email protected] pub]# awk ‘BEGIN{getline <”kecheng.dat”;print $0;}’
以上四行awk程式都是將kecheng.dat的第一行資料打印出來,結果是:zhangsan math english chinese
[[email protected] pub]# awk ‘{getline var;print $0;print var;}’ kecheng.dat
zhangsan math english chinese
lisi computer chinese english
wangwu dianzi chinese math
zhaoliu huanjing english chinese
[[email protected] pub]# awk ‘{getline var;print var;}’ kecheng.dat
lisi computer chinese english
zhaoliu huanjing english chinese
以上兩個例子可以看出來,awk和getline是分別取資料檔案中的行資料,而且是awk首先從資料檔案中取資料,後getline取下一行資料。
getlin在不同環境下影響到awk中的值的對應關係如下圖:
————————————————-
形式 設定
————————————————-
getline $0,NF,NR,FNR
getline var var,NR,FNR
getline<file $0,$1…$NF,NF
getline var<file var
cmd|getline $0,NF
cmd|getline var var
在這裡舉個例子,統計上班到達時間及遲到次數的程式。這程式每日被執行時將讀入二個檔案:員工當日上班時間的資料檔案 ( arrive.dat ) 存放員工當月遲到累計次數的檔案當程式執行執完畢後將更新第二個檔案的資料(遲到次數), 並列印當日的報表。
此程式的步驟分析如下:
[6.1] 在上班資料檔案arrive.dat之前增加一行標題 “ID Number Arrvial Time”,併產生報表輸出到檔案today_result1中。
[6.2]將today_result1上的資料按員工代號排序, 並加註執行當日日期; 產生檔案today_result2
[6.3] 將awk程式包含在一個shell script檔案中
[6.4] 在today_result2 每日報表上,遲到者之前加上”*”,並加註當日平均上班時間;產生檔案today_result3
[6.5] 從檔案中讀取當月遲到次數, 並根據當日出勤狀況更新遲到累計數。
上班時間資料檔案arrive.dat的格式是:第一列是員工代號,第二列是到達時間,內容如下:
[[email protected] pub]# cat arrive.dat
A034 7:26
A025 7:27
A101 7:32
A006 7:45
A012 7:46
A028 7:49
A051 7:51
A029 7:57
A042 7:59
A008 8:01
A052 8:05
A005 8:12
說 明:awk程式中,檔名稱today_result1的前後需要用” (雙引號)括起來,表示today_result1是一個字串常量。如果沒有用雙括號括起來,today_result1將被awk解釋為一個變數名 稱。在awk中,變數不需要事先宣告,變數的初始值為空字串(Null string) 或0。因此awk程式中如果沒有將today_result1用雙括號括起來,那麼awk將today_result1作為一個變數來使用,它的值是空字 符串,那麼在執行時造成錯誤(Unix無法開啟一個以空字串為檔名的檔案)。因此在編寫awk程式時,一定要將檔名用雙括號括起來。BEGIN是 awk 的保留字,是Pattern的一種。以BEGIN為Pattern的Actions在awk程式剛被執行尚未讀取資料檔案時被執行一次,此後便不再被執 行。本程式中若使用”>” 將資料重定向到today_result1,awk第一次執行該指令時會產生一個新檔案today_result1,再執行該指令時則把資料追加到 today_result1的檔案結尾,而不是每執行一次就開啟一次該檔案。而”>>”和“>”的差異僅在執行該指令時,如果已存在 today_result1則awk將直接把資料append在原檔案的末尾。
awk 中如何利用系統資源
awk程式中可以很容易的使用系統資源。比如1、在程式中途呼叫Shell命令來處理程式中的部分資料;2、在呼叫Shell命令後將其產生的結果交回 awk 程式(不需將結果暫存於某個檔案)。這一過程是使用awk 所提供的管道(類似於Unix中的管道,但又有些不同),和一個從awk 中呼叫Unix的Shell命令的語法來實現的。
例:承上題,將資料按員工ID排序後再輸出到檔案today_result2, 並在表頭上新增執行時的日期。
分 析:
awk 提供了和UNIX用法類似的管道命令”|”。在awk中管道的用法和含意如下:awk程式中可接受下列兩種語法:
[a語法] awk output 指令| “Shell 接受的命令” ( 如:print $1,$2 | “sort -k 1″ )
[b語法] “Shell 接受的命令” | awk input 指令 ( 如:”ls ” | getline) 。
awk的input指令只有getline 這一個輸入指令。awk的output 指令有print,printf() 二個。
在a語法中,awk所輸出的資料將轉送往Shell ,由Shell的命令進行處理。以上例而言,print所輸出的資料將經由Shell 命令”sort – k 1″排序後再輸出到螢幕(stdout)。上例awk程式中,”print $1,$2″ 可能反覆執行很多次,輸出的結果將先暫存在pipe中,等程式結束後,才會一併進行排序”sort -k 1″。須注意二點:不論print $1,$2被執行幾次,”sort -k 1″的執行時間是”awk程式結束時”,”sort -k 1″ 的執行次數是”一次”。
在b語法中,awk將先呼叫Shell命令,執行結果將通過pipe傳送到awk程式,以上例而言,awk先讓Shell執行”ls”,Shell執行後 將結果儲存在pipe中,awk的輸入指令getline再從pipe中讀取資料。使用本語法時應注意:在上例中,awk”立刻”呼叫Shell 來執行 ”ls”,執行次數是一次。getline則可能執行多次(如果pipe 中存在多行資料)。
除以上a,b兩種語法外,awk程式中其它地方如出現像”date”,”ls”…這樣的字串,awk只把它當成一般字串處理。
根據以上分析,建立awk程式指令碼內容如下:
[[email protected] pub]# cat reformat2.awk
#!/bin/awk -f
BEGIN{
“date”|getline;#在shell中執行date指令,並將結果儲存到$0中。
print “Today is”,$2,$3>”today_result2″;
print “ID Number Arrival Time”>”today_result2″;
print “=========================”>”today_result2″;
close(“today_result2″);
}
{printf(“%s/t/t%s/n”,$1,$2)|”sort -k 1 >> today_result2″;}
執行該程式指令碼:
[[email protected] pub]# awk -f reformat2.awk arrive.dat
執行後,程式將sort後的資料追加(Append)到檔案today_result2最末端。today_result2內容如下:
[[email protected] pub]# cat today_result2
Today is 06月 17日
ID Number Arrival Time
=========================
A005 8:12
A006 7:45
A008 8:01
A012 7:46
A025 7:27
A028 7:49
A029 7:57
A034 7:26
A042 7:59
A051 7:51
A052 8:05
A101 7:32
解釋說明:awk程式由三個主要部分構成:1、Pattern { Action} 指令,2、函式主體。例如:function double( x ){ return 2*x },3、註釋Comment ( 以# 開頭識別之 )。
awk 的輸入指令getline,每次讀取一列資料。如果getline之後沒接任何變數,讀入的資料將儲存到$0中,否則以所指定的變數儲儲存。
比如在執行”date”|getline後,$0 的值是2010年 06月 17日 星期四 10:43:16 CST,當$0被更新時,awk將自動更新相關的內建變數,如:$1,$2,…,NF。所以$2的值是”06月”,$3的值是“17日”。
本程式中printf() 指令會被執行12次( 因為有arrive.dat中有12行資料),在awk結束該程式時會close這個pipe ,此時才將12行資料一次送往系統,並由”sort -k 1 >> today_result2″進行處理,注意”>>”的左側一定要有空格,否則在執行的時候會報錯“sort:選項需要一個引數 -k”,awk還提供了另一種呼叫Shell命令的方法,即使用awk 函式system(“shell 命令”) ,例如:
[[email protected] pub]# awk ‘BEGIN{system(“date>date.dat”);getline<”date.dat”;print “Today is “,$2,$3}’,但使用 system( “shell 命令” ) 時,awk無法直接將執行中的資料輸出給Shell 命令。且Shell命令執行的結果也無法直接輸入到awk 中。
執行awk 程式的幾種方式
一下部分將介紹如何將awk程式直接寫在shell script 之中,這樣在執行的時候就不需要在命令列每次都輸入” awk -f program datafile” 了。script中還可包含其它Shell 命令,這樣可增加執行過程的自動化。建立一個簡單的awk程式mydump.awk,如下: {print}這個程式執行時會把資料檔案的內容print到螢幕上( 與cat功用類似 )。print 之後未接任何引數時,表示 “print $0″。如果要執行這個awk程式輸出today_result1和today_result2
的內容時,在unix命令列上,有以下幾種方式:
一、awk -f mydump.awk today_result1 today_result2
二、awk ‘{print}’ today_result1 today_result2
第二種方法將awk 程式直接寫在Shell的命令列上,這種方法僅適合awk程式較短的時候。
三、使用shell指令碼。建立一個shell指令碼mydisplay,內容如下:
[[email protected] pub]# cat mydisplay
#!/bin/bash # 注意awk 與’ 之間須有空白隔開
awk ‘{print}’ $*# 注意’ 與$* 之間須有空白隔開
在執行mydisplay 之前,需要給它新增可執行許可權,首先執行如下命令: [[email protected] pub]# chmod +x mydisplay,這樣mydisplay才有可執行許可權,[[email protected] pub]# ./mydisplay today_result1 today_result2就可以執行了,當然,如果沒有給他可執行許可權的話,也可以使用一下方法來執行:[[email protected] pub]# bash mydisplay today_result1
today_result2
說明:在script檔案mydisplay 中,指令”awk”與第一個’ 之間須有空格(Shell中並無” awk’ “指令)。第一個’用來通知Shell其後為awk程式,第二個’ 則表示awk 程式結束。所以awk程式中一律以”括住字串或字元,而不能使用’括住字串或字元,以免引起Shell混淆。$* 是shell script 中的用法,可用來代表命令列上”mydisplay之後的所有引數”。例如執行:[[email protected] pub]# ./mydisplay today_result1
today_result2事實上Shell 已先把該指令轉換成:awk ‘ { print} ‘ today_result1 today_result2 。本例中,$*代表”today_result1 today_result2″。在Shell的語法中, $1代表第一個引數,$2 代表第二個引數。當不確定命令列上的引數個數時,可使用$*代表。awk命令列上可同時指定多個數據檔案。以awk -f dump.awk today_result1 today_result2hf 為例,awk會先處理today_result1,再處理today_result2,如果檔案不存在或無法開啟,會提示相應的錯誤,比如awk:
(FILENAME=today_result1 FNR=14) fatal: cannot open file `today_result’ for reading (沒有那個檔案或目錄)。
有些awk程式”僅” 包含以BEGIN為Pattern的指令,這種awk 程式執行時不須要資料檔案,如果在命令列上指定一個不存在的資料檔案,awk不會產生”無法開啟檔案”的錯誤(事實上awk並未開啟該檔案) 。例如執行:[[email protected] pub]#awk ‘BEGIN {print “Hello,World!!”} ‘ file_no_exist ,該程式中僅包含以BEGIN 為Pattern的指令,awk 執行時並不會開啟任何資料檔案; 所以不會因不存在檔案file_no_exit
產生” 無法開啟檔案”的錯誤。
awk會將Shell 命令列上awk程式(或 -f 程式檔名)之後的所有字串,視為將輸入awk進行處理的資料檔案檔名。如果執行awk 的命令列上”未指定任何資料檔案檔名”,則將stdin作為資料來源,直到輸入end of file( Ctrl-D )為止。比如執行如下命令:
[[email protected] pub]#awk -f mydump.awk #(未接任何資料檔案檔名)
或
[[email protected] pub]#./mydisplay #(未接任何資料檔案檔名)
會發現:此後鍵入的任何資料將逐行復印一份顯示到螢幕上,這種情況是因為執行時沒有指定資料檔案檔名,awk 便以stdin(鍵盤上的輸入)做為資料來源。那麼我們可利用這個特點,設計與awk即時聊天的程式:mrgreen: 。
改變awk 切割欄位的方式& 自定義函式
awk不僅能自動分割欄位,也允許使用者改變其欄位切割方式以適應各種格式的需要。
例題:承接6.2的例子,如果八點為上班時間,那麼請在遲到的記錄前加上“*”,並計算平均上班時間。
分 析:八點整到達者,不算遲到,所以只根據到達的小時數來判斷是不夠得,還需要應參考到達時的分鐘數。如果“將到達時間轉換成以分鐘為單位”,進行判斷是否 遲到和計算到達平均時間比較容易。到達時間($2)的格式為dd:dd 或d:dd,數字當中含有一個“:”,awk無法對這些資料進行處理 (注:awk中字串”26″與數字26,並無差異,可直接做字串或數學運算,這是awk重要特色之一。 但awk對文字數字交雜的字串無法正確進行數學運算),那麼可以使用一下方法解決:
[方法一] 對到達時間($2) d:dd 或dd:dd 進行字串運算,分別取出到達的小時數和分鐘數。 首先判斷到達小時數是一位還是兩位字元,再呼叫函式分別擷取分鐘數和小時數,需要用到下列awk字串函式:
length( 字串) :返回該字串的長度;
substr( 字串,起始位置,長度) :返回從起始位置起,指定長度之子字串;若未指定長度,則返回從起始位置到字串末尾的子字串。
所以:小時數=substr( $2,1,length($2) – 3 ) ,分鐘數=substr( $2,length($2) – 2)
[[email protected] pub]# awk ‘BEGIN{“date”|getline;print substr($5,1,2)*60+substr($5,4,2);}’
[方 法二]改變輸入列欄位的切割方式,使awk切割欄位後分別將小時數及分鐘數隔開於二個不同的欄位。欄位分隔字元FS (field seperator) 是awk 的內建變數,其預設值是空白及tab。awk每次切割欄位時都會先參考FS的內容。若把”:”也當成分隔字元,則awk 便能自動把小時數及分鐘數分隔成不同的欄位。故令FS = “[ /t:]+” (注: [ /t:]+ 是一個正則表示式Regular Expression ) Regular Expression中使用中括號[]
表示一個字元集合,用以表示任意一個位於兩中括號間的字元。所以可用”[ /t:]“表示一個空格, tab 或”:” ,Regular Expression中”+” 表示其前方的字元可出現一次或一次以上。所以”[ /t:]+” 表示由一個或多個”空格,tab 或 : ” 所組成的字串。設定FS =”[ /t:]+” 後,資料行如:”1034 7:26″ 將被分割成3個欄位,$1是1034,$2是7,$3是26,顯然,awk程式中使用方法二要比方法一更簡潔方便。所以本例中使用方法二,來介紹改變欄位 切割方式的用法。
編寫awk程式reformat3,如下:
[[email protected] pub]# cat reformat3.awk
#!/bin/bash
awk ‘BEGIN{
FS=”[ /t:]+”;
“date”|getline;
print “Today is”,$2,$3 > “today_result3″;
print “==================” > “today_result3″;
print “ID Number Arrival Time” > “today_result3″;
close(“today_result3″);
}
{
arrival=HM_TO_M($2,$3);
printf(“%s/t/t%s:%s %s/n”,$1,$2,$3,arrival>”480″?”*”:” “)|”sort -k 1 >> today_result3″;
total+=arrival;
}
END{
close(“sort -k 1 >> today_result3″);
printf(“Average arrival time: %d:%d/n”,total/NR/60, total/NR%60) >> “today_result3″;
close(“today_result3″);
}
function HM_TO_M(hour,min){ return hour*60 + min }’ $*
並執行如下指令 :
[[email protected] pub]# bash reformat3.awk arrive.dat
執行後,檔案 today_result3 的內容如下:
[[email protected] pub]# cat today_result3
Today is 06月 17日
==================
ID Number Arrival Time
A005 8:12 *
A006 7:45
A008 8:01 *
A012 7:46
A025 7:27
A028 7:49
A029 7:57
A034 7:26
A042 7:59
A051 7:51
A052 8:05 *
A101 7:32
Average arrival time: 7:49
說明:awk 中允許自定義函式,函式定義方式可參考本程式,function是awk的保留字。HM_TO_M( ) 這函式負責將傳入的小時和分鐘數轉換成以分鐘為單位的時間。在printf()中使用的arrival >480 ? “*” :” “是一個三元運算子,如果arrival 大於480則return “*” ,否則return ” “。
% 是awk的運算子(operator),作用與C 語言中的% 相同(取餘數)。
NR(Number of Record) 為awk 的內建變數,表示awk執行該程式後所讀入的記錄筆數。
close的語法有二種:close( filename ) 和close( 置於pipe之前的command ) 。本程式使用了兩個close( ) 指令:指令close( “sort -k 1 >> today_result3″ ),意思是close程式置於”sort -k 1 >> today_result3 ” 之前的Pipe , 並立刻呼叫Shell 來執行”sort -k 1 >> today_result3″。 因為 Shell 排序後的資料也要寫到
today_result3, 所以awk必須先關閉 使用中的today_result3 以使 Shell 正確將排序後的資料追加到 today_result3否則2個不同的 process 同時開啟一個檔案進行輸出將會 產生不可預期的結果。 讀者應留心上述兩點,才可正確控制資料輸出到檔案中的順序。指令close(“sort -k 1 >> today_result3″)中字串 “sort +0n >> today_result3″ 必須與pipe |後方的Shell Comman名稱一字不差,否則awk將視為二個不同的pipe。
?使用getline 來讀取資料
範例: 承上題,從檔案中讀取當月遲到次數,並根據當日出勤狀況更新遲到累計數。(按不同的月份累計於不同的檔案)
分 析: 程式中自動抓取系統日期的月份名稱,連線上”late.dat”, 形成累計遲到次數的檔名稱(如:09月late.dat,。。。), 並以變數late_file記錄該檔名。累計遲到次數的檔案中的資料格式為:員工代號(ID) 遲到次數。例如,執行本程式前檔案09月late.dat 的內容如下:
[[email protected] pub]# cat late.dat
A005 2
A006 1
A008 2
A012 0
A025 0
A028 1
A029 2
A034 0
A042 0
A051 0
A052 3
A101 0
編寫程式reformat4 如下:[[email protected] pub]# cat reformat4.awk
#!/bin/bash
awk ‘BEGIN{
sys_sort=”sort -k 1 >> today_result4″;
result=”today_result4″;
FS=”[ /t:]+”;#改變欄位切割的方式
“date”|getline;#令Shell執行”date”;getline讀取結果,並以$0記錄結果
print “Today is”,$2,$3 > result;
print “=======================”>result;
print “ID Number Arrival Time”> result;
close(result);
late_file=$2″late.dat”;
while(getline<late_file >0) #從檔案按中讀取遲到資料,並用陣列cnt[]記錄。
cnt[$1]=$2 #陣列cnt[]中以員工代號為下標,所對應的值為該員工之遲到次數
close(late_file)
}
{arrival=HM_TO_M($2,$3);#已更改欄位切割方式,$2表小時數,$3表分鐘數
if(arrival>480)
{mark=”*”; #若當天遲到,應再增加其遲到次數,令mark為”*”
cnt[$1]++;}
else
mark=” “;
message=cnt[$1]?cnt[$1]“times”:” “;# message用以顯示該員工的遲到累計數,若未曾遲到message為空字串
printf(“%s/t/t%2s:%2s %5s %s/n”,$1,$2,$3,mark,message)|sys_sort;
total+=arrival;
}
END{
close(result);
close(sys_sort);
printf(“Arrivage arrival time: %d:%d/n”,total/NR/60,total/NR%60) >> result;
for(any in cnt) #將陣列cnt[]中新的遲到資料寫回檔案中
print any,cnt[any] > late_file;
}
function HM_TO_M(hour,minute){return hour*60+minute;}
‘ $*
執行後結果如下:
[[email protected] pub]# bash reformat4.awk arrive.dat
[[email protected] pub]# cat today_result4
Today is 06月 18日
=======================
ID Number Arrival Time
A005 8:12 * 1times
A006 7:45
A008 8:01 * 1times
A012 7:46
A025 7:27
A028 7:49
A029 7:57
A034 7:26
A042 7:59
A051 7:51
A052 8:05 * 1times
A101 7:32
Arrivage arrival time: 7:49
06月late.dat的內容如下:
[[email protected] pub]# cat 06月late.dat
A028
A029
A012
A005 1
A042
A051
A006
A101
A052 1
A025
A034
A008 1
說 明:由於檔案06月late.dat中儲存了一些資料內容,在這裡我沒有做更多的修改,所以每次執行[[email protected] pub]# bash reformat4.awk arrive.dat的時候,遲到次數都會增加,您可以根據實際情況,再做一下修改,做的更完美。
late_file是一變數,記錄遲到次數的檔案的檔名。late_file值由兩部分構成,前半部是當月月份名稱(由呼叫”date”取得)後半部固 定為”late.dat” 如:06月late.dat。指令getline < late_file 表示從late_file所代表的檔案中讀取一筆記錄,並存放於$0。awk會自動對新置入$0 的資料進行欄位分割,之後程式中可用$1, $2,。。來表示資料的第一欄,第二 欄,。。,
注: 有少數awk版本不容許使用者自行將資料置於$0,這種情況可改用gawk或nawk。執行getline指令時, 若成功讀取記錄,它會返回1;若遇到檔案結束,它返回0;無法開啟檔案則返回-1。利用 while( getline < filename >0 ) {…}可讀入檔案中的每一筆資料並進行處理。這是awk 中使用者自行讀取資料檔案的一個重要模式。陣列 cnt[ ] 以員工ID,下標(index)對應值表示其遲到的次數。執行結束後,利用 for(Variable in array ){。。。}的語法
for( any in cnt ) print any, cnt[any] > late_file 將更新過的遲到資料重新寫回記錄遲到次數的檔案
awk 每次從資料檔案中只讀取一行資料進行處理,這是因為awk中有一個內建變數RS(Record Separator) ,RS將檔案中的資料分隔成以行為單位的記錄record。RS預設值以”/n”(跳行符號)分隔資料檔案中的資訊,所以預設情況下awk 中一行資料就是一行Record。但有些檔案中一行Record涵蓋了多行資料,這種情況下不能再以”/n” 來分隔Records。最常使用的方法是相鄰的Records之間改用一個空白行來分隔。在awk程式中,令RS= “”(空字串)後,awk把會空白行當成來檔案中Record的分隔符。顯然awk對RS=”"另有深意,簡單來說是這樣的,當RS=”"
時:多個相鄰的空白行,awk僅作為一個Record Saparator(awk不會在多個相鄰的空白行之中選取一行做為空的Record) ;awk會略過(skip)檔案頭和檔案尾的空白行,所以不會因為有這樣的空白行,造成awk多讀了二行空的資料。下面舉個例子看一下,首先建立一個數據 檔案myfreelinux.dat,內容如下:
[[email protected] pub]# cat myfreelinux.dat
wanger
linux_basic
lisan
linux_server
windows_server
zhaosi
awk_tools
grub
regular_expression
該檔案的開頭有3行空白行, 各行Record之間分別用2個和1個空白行隔開。那麼下面,通過幾個例子來看一下。首先編輯一個awk程式指令碼report1.awk,內容如下:[[email protected] pub]# cat report1.awk
#!/bin/sh
awk ‘BEGIN{
FS=”/n”;
RS=”";
split(“one:.two:.three:.four:.five:.six:.seven:.eight:.nine:.ten:.”,number,”.”);
}
{
printf(“/n%s reporter is : %s/n”,number[NR],$1);
for(i=2;i<=NF;i++)
printf(“%d %s/n”,i-1,$i);
}’ $*
執行該程式指令碼和產生的結果如下:
[[email protected] pub]# bash report1.awk myfreelinux.dat
one: reporter is : wanger
1 linux_basic
two: reporter is : lisan
1 linux_server
2 windows_server
three: reporter is : zhaosi
1 awk_tools
2 grub
3 regular_expression
解釋說明:上面這個程式的欄位分隔字元是( FS= “/n” ),這樣的話一行資料就是一個field,而且RS=“”,所以這三個使用者的記錄是通過空行來分隔的。那麼awk讀入的第一行Record 為
wanger
linux_basic
其中$1的值是”wanger”,$2的值是:“ linux_basic”,程式中的number[ ]是一個數組(array),用來記錄英文數字,比如number[1]=one:,number[2]=two:等等,這個是使用awk的字串函 數split()來把英文數字放進陣列number[ ]中的。
函式split( )用法如下:
split( 原字串,陣列名,分隔字元(field separator) )
awk將根據指定的分隔字元(field separator)分隔原字串成一個個的欄位(field), 並將各欄位記錄到陣列中。
在執行編寫的awk程式時,awk會自動從資料檔案中讀取資料並進行處理,直到檔案結束。實際上,只要將awk讀取資料的來源改成鍵盤輸入,那麼就可以設計與awk 互動的程式了。
首先看一個互動的程式。這個系程式能夠實現輸入一個英文單詞,程式打印出該詞對應的漢語意思,並繼續等待使用者輸入新的英文單詞。首先編輯一個數據文件data.dat,內容如下:
[[email protected] pub]# cat data.dat
man 男人
girl 女孩
boy 男孩
rose 玫瑰
apple 蘋果
banana 香蕉
編寫一個互動的awk程式,內容如下:
[[email protected] pub]# cat china-eng.awk
#!/bin/bash
awk ‘BEGIN{
while(getline<ARGV[1])
{
English[++n]=$1;#從資料檔案中讀取需要使用的資料儲存在兩個陣列中
Chinese[n]=$2;#n最後的值作為題目數量,在question中使用
}
ARGV[1]=”-”;# “-”表示由stdin(鍵盤輸入)
srand(); # 以系統時間為隨機數啟動的種子
question(); #產生考題
}
{#awk讀入資料,即回答的答案
if($1!=English[ind]) print “Try again!”
else {print “/n You are right!! Press Enter to continue……”;
getline;
question();
}
}
function question()
{ind=int(rand()*n)+1;#以隨機數選取考題
system(“clear”); #系統清屏
print “Press /”ctrl+d/” to exit”;
printf(“/n %s”,Chinese[ind] ” 的英文字是:”)}’ $*
下面執行一下這個程式:
[[email protected] pub]# bash china-eng.awk data.dat
Press “ctrl+d” to exit
香蕉 的英文字是:apple
You are right!! Press Enter to continue……
Press “ctrl+d” to exit
男人 的英文字是:men
Try again!
man
You are right!! Press Enter to continue……
說明: 引數data.dat (ARGV[1])是儲存考題資料的資料檔案檔案,awk從資料檔案上取得資料後將英文資料儲存到English的陣列中,將中文資料儲存到 Chinese的陣列中,然後將ARGV[1] 改成”-”,”-” 表示從stdin鍵盤讀入資料,對於ARGV在awk的第八部分中有很多說明。對於srand();
# 以系統時間為隨機數啟動的種子,在這個程式裡,其實沒有多大的用處,作為一個知識點,先介紹一下。
在BEGIN中,最後一行是question(),此函式是自定義的一個函式,此函式首先產生一個隨機數,是通過 ind=int(rand()*n)+1,這個語句產生的,對於n,和資料檔案有關,在這裡,資料檔案只有六行,所以n為6,對於隨機函式取整後+1,是 因為English和Chinese這兩個陣列的下表是從1開始的,而rand()函式產程的數值從0~1,所以int(rand()*n)的值是從 0~5,+1後的下標才和兩個陣列的下標值的範圍相同。system(“clear”)是awk呼叫系統的清屏函式。printf(“/n
%s”,Chinese[ind] ” 的英文字是:”)是輸出一個漢語單詞,等待使用者輸入英文單詞,輸入後的資料儲存在$1中,並和同下標的English陣列中的資料比較,如果正 確, Press Enter to continue……提示按任何鍵退出,否則會提示再輸入一次答案,知道答對為止,答對後,會再次呼叫question()函式,產生下一個問題,知道在 鍵盤輸入結束符號 (End of file)是ctrl+d,當awk 讀到ctrl+d時就停止由鍵盤讀取資料,程式結束。
awk 的數學函式中提供兩個與隨機數有關的函式。
srand( ):以當前的系統時間作為隨機數的種子
rand( ) :返回介於0與1之間的(近似)隨機數值。
在linux/unix中大部分的應用程式都允許使用者在命令之後增加一些引數,在執行awk 程式是,也可以在awk程式後增加一些引數,這些引數一般是用來指定資料檔案的檔名。這裡,我們看一下awk程式是如何使用這些引數的。 建立檔案analyse.awk,內容如下:
[email protected] pub]# cat analyse.awk
#!/bin/bash
awk ‘BEGIN{
for(i=0;i<ARGC;i++)
print ARGV[i];# 依次印出awk所記錄的引數
}’ $*
執行結果如下:
[[email protected] pub]# bash analyse.awk first-arg second-arg
awk
first-arg
second-arg
解釋說明:ARGC,ARGV[ ]是awk的內建變數。
ARGC :是一整數,代表命令列上除了選項-v, -f 及其對應的引數之外所有引數的個數。
ARGV[ ] 是一字串陣列,ARGV[0],ARGV[1],。。。ARGV[ARGC-1]分別代表命令列上相對應的引數。
比如在這裡執行的命令[[email protected] pub]# bash analyse.awk first-arg second-arg,ARGC的值是3,ARGV[0]是”awk”,ARGV[1]的值為”first-arg”, ARGV[2]的值是”second-arg”。
再比如#awk -vx=21-f program fir-data sec-data
和
#awk ‘{ print $1 ,$2 }’ fir-data sec-data
這 兩條ARGC 值都是3,ARGV[0]是”awk”,ARGV[1]是”fir-data”,ARGV[2]是”sec-data”,命令列上的”-f program”,” -vx=21″,程式部分’{ print $1, $2}’ 都不會被列入ARGC和ARGV[ ]中。
awk 利用ARGC 來判斷要開啟的資料檔案的個數,但是使用者可以強行更改ARGC的值;比如將ARGC的值被使用者設定為1,那麼awk將被矇騙,誤以為命令列上沒有資料檔案 檔案, 所以不會以 ARGV[1],ARGV[2]等作為檔名來開啟檔案並讀取資料;但是在程式中可以使用ARGV[1],ARGV[2]等變數來取得命令列 上資料檔案的資料。
現在有一個awk程式內容如下:
[[email protected] pub]# cat test1.awk
#!/bin/awk -f
BEGIN{
for(i=0;i<ARGC;i++)
print ARGV[i];
}
執行以上程式的結果如下:
[[email protected] pub]# awk -f test1.awk arrive.dat today_result1
awk
arrive.dat
today_result1
加入將test1.awk的內容更改成test2.awk的內容如下:
[[email protected] pub]# cat test2.awk
#!/bin/awk -f
BEGIN{
number=ARGC; #用number 記住實際的引數個數
ARGC=2;#設定ARGC=2,awk將以為只有一個資料檔案
for(i=0;i<ARGC;i++)
print ARGV[i];
}
執行並檢視執行結果:
[[email protected] pub]# awk -f test2.awk arrive.dat today_result1 today_result2
awk
arrive.dat
這個時候會發現,雖然同樣ARGC=3,但是人為的設定ARGC=2,後,awk在執行的時候,只認為有一個引數arrive.dat。
將test2.awk修改成以下內容:
[[email protected] pub]# cat test3.awk
#!/bin/awk -f
BEGIN{
number=ARGC;
ARGC=2;
for(i=0;i<ARGC;i++)
print ARGV[i];
for(i=ARGC;i<number;i++)
print ARGV[i];
}
執行並檢視執行結果如下:
[[email protected] pub]# awk -f test3.awk arrive.dat today_result1 today_result2
awk
arrive.dat
today_result1
顯 然,通過修改ARGC可以修改awk能夠識別的引數的個數,但是實際存在的ARGV的內容,仍然可以訪問的。比如在這裡ARGC設定為2後,awk只能打 開ARGV[1]=arrive.dat,但是我們可以使用ARGV[2],ARGV[3]取得命令列上的引數 today_result1,today_result2。
awk 通過判斷模式(Pattern)的值來決定是否執行其後對應的動作(Actions)。首先來看一下awk中幾個常見的模式,在前十部分中,有一些模式已經做了介紹,在這裡再總結一下:
1、BEGIN是awk 的保留字,是一種特殊的模式。
BEGIN 成立(其值為true)的時機是:“awk 程式一開始執行,還沒有讀取任何資料之前”。 所以在BEGIN{ Actions} 語法中,Actions只在程式一開始執行時被執行一次。當awk 從資料檔案讀入資料行後,BEGIN 便不再成立,所以不論資料檔案有多少資料行資料,Actions也不會被再次執行。一般情況下,把“與資料檔案內容無關”和“只需執行ㄧ次”的部分放在以 BEGIN 為模式的Actions中。
比如:[[email protected] pub]# cat BEGIN.awk
#!/bin/awk -f
BEGIN{
FS=”[ /t:]+”; #設定awk分割欄位的預設方式
RS=”" #設定awk分割資料行的方式
count=10; #設定count的初始值
print “====This is title====” #列印標題行
}
有些awk程式不需要讀入任何資料行,這情況可把整個程式寫在以BEGIN 為模式的函式中,有時候也可以寫在以END為模式的函式中,END後面介紹。
例如:[[email protected] pub]# awk ‘BEGIN{print “hello world!”}’會打印出“hello world!”,在awk語句後面則不需要有資料檔案,這就是BEGIN模式的特點。
2、END模式
END是awk 的保留字,也是一個特殊的模式。END 成立(其值為true)的時機和BEGIN成立的時機正好相反,END成立的時機是:“awk 處理完所有資料, 即將離開程式時”,在平常讀入資料行時,END模式不成立,所以END對應的Actions 並不被執行;只有當awk處理完所有資料後,END對應的Actions才會被執行。
和BEGIN模式一樣,不管資料檔案有多少行資料,該Actions只被執行一次。
3、關係表示式
awk 中提供了很多關係運算符(Relation Operator)
運算子 含意
> 大於
< 小於
>= 大於或等於
<= 小於或等於
== 等於
!= 不等於
~ 匹配 match
!~ 不匹配not match
以上關係運算符除~(match)與!~(not match