<b>十一</b><b>. awk程式設計:</b>
<b> 1. </b><b>變量:</b>
在awk中變量無須定義即可使用,變量在指派時即已經完成了定義。變量的類型可以是數字、字元串。根據使用的不同,未初始化變量的值為0或空白字元串" ",這主要取決于變量應用的上下文。下面為變量的指派負号清單:
<b>符号</b>
<b>含義</b>
<b>等價形式</b>
=
a = 5
+=
a = a + 5
a += 5
-=
a = a - 5
a -= 5
*=
a = a * 5
a *= 5
/=
a = a / 5
a /= 5
%=
a = a % 5
a %= 5
^=
a = a ^ 5
a ^= 5
/> awk '$1 ~ /Tom/ {Wage = $2 * $3; print Wage}' filename
該指令将從檔案中讀取,并查找第一個域字段比對Tom的記錄,再将其第二和第三個字段的乘積指派給自定義的Wage變量,最後通過print指令将該變量列印輸出。
/> awk ' {$5 = 1000 * $3 / $2; print}' filename
在上面的指令中,如果$5不存在,awk将計算表達式1000 * $3 / $2的值,并将其指派給$5。如果第五個域存在,則用表達式覆寫$5原來的值。
我們同樣也可以在指令行中定義自定義的變量,用法如下:
/> awk -F: -f awkscript month=4 year=2011 filename
這裡的month和year都是自定義變量,且分别被指派為4和2000,在awk的腳本中這些變量将可以被直接使用,他們和腳本中定義的變量在使用上沒有任何差別。
除此之外,awk還提供了一組内建變量(變量名全部大寫),見如下清單:
<b>變量名</b>
<b>變量内容</b>
ARGC
指令行參數的數量。
ARGIND
指令行正在處理的目前檔案的AGV的索引。
ARGV
指令行參數數組。
CONVFMT
轉換數字格式。
ENVIRON
從shell中傳遞來的包含目前環境變量的數組。
ERRNO
當使用close函數或者通過getline函數讀取的時候,發生的重新定向錯誤的描述資訊就儲存在這個變量中。
FIELDWIDTHS
在對記錄進行固定域寬的分割時,可以替代FS的分隔符的清單。
FILENAME
目前的輸入檔案名。
FNR
目前檔案的記錄号。
FS
輸入分隔符,預設是空格。
IGNORECASE
在正規表達式和字元串操作中關閉大小寫敏感。
NF
目前檔案域的數量。
NR
目前檔案記錄數。
OFMT
數字輸出格式。
OFS
輸出域分隔符。
ORS
輸出記錄分隔符。
RLENGTH
通過match函數比對的字元串的長度。
RS
輸入記錄分隔符。
RSTART
通過match函數比對的字元串的偏移量。
SUBSEP
下标分隔符。
/> cat employees2
Tom Jones:4424:5/12/66:543354
Mary Adams:5346:11/4/63:28765
Sally Chang:1654:7/22/54:650000
Mary Black:1683:9/23/44:336500
/> awk -F: '{IGNORECASE = 1}; $1 == "mary adams" { print NR, $1, $2, $NF}' employees2
2 Mary Adams 5346 28765
/> awk -F: ' $1 == "mary adams" { print NR, $1, $2, $NF}' employees2
沒有輸出結果。
當IGNORECASE内置變量的值為非0時,表示在進行字元串操作和處理正規表達式時關閉大小寫敏感。這裡的"mary adams"将比對檔案中的"Mary Admams"記錄。最後print列印出第一、第二和最後一個域。需要說明的是NF表示目前記錄域的數量,是以$NF将表示最後一個域的值。
awk在動作部分還提供了BEGIN塊和END塊。其中BEGIN動作塊在awk處理任何輸入檔案行之前執行。事實上,BEGIN塊可以在沒有任何輸入檔案的條件下測試。因為在BEGIN塊執行完畢以前awk将不讀取任何輸入檔案。BEGIN塊通常被用來改變内建變量的值,如OFS、RS或FS等。也可以用于初始化自定義變量值,或列印輸出标題。
/> awk 'BEGIN {FS = ":"; OFS = "\t"; ORS = "\n\n"} { print $1,$2,$3} filename
上例中awk在處理檔案之前,已經将域分隔符(FS)設定為冒号,輸出檔案域分隔符(OFS)設定為制表符,輸出記錄分隔符(ORS)被設定為兩個換行符。BEGIN之後的動作子產品中如果有多個語句,他們之間用分号分隔。
和BEGIN恰恰相反,END子產品中的動作是在整個檔案處理完畢之後被執行的。
/> awk 'END {print "The number of the records is " NR }' filename
awk在處理輸入檔案之後,執行END子產品中的動作,上例中NR的值是讀入的最後一個記錄的記錄号。
/> awk '/Mary/{count++} END{print "Mary was found " count " times." }' employees2
Mary was found 2 times.
/> cat testfile
northwest NW Charles Main 3.0 .98 3 34
western WE Sharon Gray 5.3 .97 5 23
southwest SW Lewis Dalsass 2.7 .8 2 18
southern SO Suan Chin 5.1 .95 4 15
southeast SE Patricia Hemenway 4.0 .7 4 17
eastern EA TB Savage 4.4 .84 5 20
northeast NE AM Main Jr. 5.1 .94 3 13
north NO Margot Weber 4.5 .89 5 9
central CT Ann Stephens 5.7 .94 5 13
/> awk '/^north/{count += 1; print count}' testfile #如記錄以正則north開頭,則建立變量count同時增一,再輸出其值。
1
2
3
#這裡隻是輸出前三個字段,其中第七個域先被指派給變量x,在自減一,最後再同時列印出他們。
/> awk 'NR <= 3 {x = $7--; print "x = " x ", $7 = " $7}' testfile
x = 3, $7 = 2
x = 5, $7 = 4
x = 2, $7 = 1
#列印NR(記錄号)的值在2--5之間的記錄。
/> awk 'NR == 2,NR == 5 {print "The record number is " NR}' testfile
The record number is 2
The record number is 3
The record number is 4
The record number is 5
#列印環境變量USER和HOME的值。環境變量的值由父程序shell傳遞給awk程式的。
/> awk 'BEGIN { print ENVIRON["USER"],ENVIRON["HOME"]}'
root /root
#BEGIN塊兒中對OFS内置變量重新指派了,是以後面的輸出域分隔符改為了\t。
/> awk 'BEGIN { OFS = "\t"}; /^Sharon/{ print $1,$2,$7}' testfile
western WE 5
#從輸入檔案中找到以north開頭的記錄count就加一,最後在END塊中輸出該變量。
/> awk '/^north/{count++}; END{print count}' testfile
<b> 2. 重新定向:</b>
在動作語句中使用shell通用的重定向輸出符号">"就可以完成awk的重定向操作,當使用>的時候,原有檔案将被清空,同時檔案持續打開,直到檔案被明确的關閉或者awk程式終止。來自後面的列印語句的輸出會追加到前面内容的後面。符号">>"用來打開一個檔案但是不清空原有檔案的内容,重定向的輸出隻是被追加到這個檔案的末尾。
/> awk '$4 >= 70 {print $1,$2 > "passing_file"}' filename #注意這裡的檔案名需要用雙引号括起來。
#通過兩次cat的結果可以看出>和>>的差別。
/> awk '/north/{print $1,$3,$4 > "districts" }' testfile
/> cat districts
northwest Joel Craig
northeast TJ Nichols
north Val Shultz
/> awk '/south/{print $1,$3,$4 >> "districts" }' testfile
southwest Chris Foster
southern May Chin
southeast Derek Jonhson
awk中對于輸入重定向是通過getline函數來完成的。getline函數的作用是從标準輸入、管道或者目前正在處理的檔案之外的其他輸入檔案獲得輸入。他負責從輸入獲得下一行的内容,并給NF、NR和FNR等内建變量指派。如果得到一個記錄,getline就傳回1,如果達到檔案末尾就傳回0。如果出現錯誤,如打開檔案失敗,就傳回-1。
/> awk 'BEGIN { "date" | getline d; print d}'
Tue Nov 15 15:31:42 CST 2011
上例中的BEGIN動作子產品中,先執行shell指令date,并通過管道輸出給getline,然後再把輸出指派給自定義變量d并列印輸出它。
/> awk 'BEGIN { "date" | getline d; split(d,mon); print mon[2]}'
Nov
上例中date指令通過管道輸出給getline并指派給d變量,再通過内置函數split将d拆分為mon數組,最後print出mon數組的第二個元素。
/> awk 'BEGIN { while("ls" | getline) print}'
employees
employees2
testfile
指令ls的輸出傳遞給getline作為輸入,循環的每個反複,getline都從ls的結果中讀取一行輸入,并把他列印到螢幕。
/> awk 'BEGIN { printf "What is your name? "; \
getline name < "/dev/tty"}\
$1 ~ name {print "Found" name " on line ", NR "."}\
END {print "See ya, " name "."}' employees2
What is your name? Mary
Found Mary on line 2.
See ya, Mary.
上例先是列印出BEGIN塊中的"What is your name? ",然後等待使用者從/dev/tty輸入,并将讀入的資料指派給name變量,之後再從輸入檔案中讀取記錄,并找到比對輸入變量的記錄并列印出來,最後在END塊中輸出結尾資訊。
/> awk 'BEGIN { while(getline < "/etc/passwd" > 0) lc++; print lc}'
32
awk将逐行讀取/etc/passwd檔案中的内容,在達到檔案末尾之前,計數器lc一直自增1,當到了末尾後列印lc的值。lc的值為/etc/passwd檔案的行數。
由于awk中同時打開的管道隻有一個,那麼在打開下一個管道之前必須關閉它,管道符号右邊可以通過可以通過雙引号關閉管道。如果不關閉,它将始終保持打開狀态,直到awk退出。
/> awk {print $1,$2,$3 | "sort -4 +1 -2 +0 -1"} END {close("sort -4 +1 -2 +0 -1") } filename
上例中END子產品中的close顯示關閉了sort的管道,需要注意的是close中關閉的指令必須和當初打開時的完全比對,否則END子產品産生的輸出會和以前的輸出一起被sort分類。<b> 3. 條件語句:</b>
awk中的條件語句是從C語言中借鑒來的,見如下聲明方式:
if (expression) {
statement;
... ...
}
/> awk '{if ($6 > 50) print $1 "Too hign"}' filename
/> awk '{if ($6 > 20 && $6 <= 50) { safe++; print "OK}}' filename
} else {
statement2;
/> awk '{if ($6 > 50) print $1 " Too high"; else print "Range is OK" }' filename
/> awk '{if ($6 > 50) { count++; print $3 } else { x = 5; print $5 }' filename
statement1;
} else if (expression1) {
statement3;
/> awk '{if ($6 > 50) print "$6 > 50" else if ($6 > 30) print "$6 > 30" else print "other"}' filename
<b> 4. 循環語句:</b>
awk中的循環語句同樣借鑒于C語言,支援while、do/while、for、break、continue,這些關鍵字的語義和C語言中的語義完全相同。
<b> 5. 流程控制語句:</b>
next語句是從檔案中讀取下一行,然後從頭開始執行awk腳本。
exit語句用于結束awk程式。它終止對記錄的處理。但是不會略過END子產品,如果exit()語句被指派0--255之間的參數,如exit(1),這個參數就被列印到指令行,以判斷退出成功還是失敗。