10、循環結構
循環結構
awk 的 while 循環結構,它等同於相應的 C 語言 while
循環。
awk 還有 "do...while" 循環,它在代碼塊結尾處對條件求值,而不象標準 while
循環那樣在開始處求值。
它類似於其它語言中的 "repeat...until" 循環。
示例:
do...while 示例 {
count=1
do {
print "I get printed at least once no matter what"
} while ( count != 1 )
}
與一般的 while 循環不同,由於在代碼塊之後對條件求值,"do...while" 循環永遠都至少執行一次。
換句話說,當第一次遇到普通
while 循環時,如果條件爲假,將永遠不執行該循環。
for 循環
awk 允許創建 for 循環,它就象 while 循環,也等同於 C 語言的 for 循環:
for ( initial
assignment; comparison; increment ) {
code block
}
示例:
for ( x = 1; x <= 4; x++ ) {
print "iteration",x
}
此段代碼將打印:
iteration 1
iteration 2
iteration 3
iteration 4
break 和 continue
此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制
awk 的循環結構。以下是迫切需要 break 語句的代碼片斷:
while 死循環
while (1) {
print "forever and ever..."
}
因爲 1
永遠代表是真,這個 while 循環將永遠運行下去。以下是一個只執行十次的循環:
break 語句示例 x=1
while(1) {
print "iteration",x
if ( x == 10 )
{
break
}
x++
}
這裏,break 語句用於“逃出”最深層的循環。"break" 使循環立即終止,並繼續執行循環代碼塊後面的語句。
continue 語句補充了
break,其作用如下:
x=1
while (1) {
if ( x == 4 ) {
x++
continue
}
print "iteration",x
if ( x > 20 )
{
break
}
x++
}
這段代碼打印 "iteration 1" 到 "iteration 21","iteration 4" 除外。如果迭代等於 4,則增加 x 並調用
continue 語句,該語句立即使 awk 開始執行下一個循環迭代,而不執行代碼塊的其餘部分。如同 break 一樣,continue 語句適合各種 awk
迭代循環。在 for 循環主體中使用時,continue 將使循環控制變量自動增加。以下是一個等價循環:
for ( x=1; x<=21;
x++ ) {
if ( x == 4 ) {
continue
}
print
"iteration",x
}
在 while 循環中時,在調用 continue 之前沒有必要增加 x,因爲 for 循環會自動增加 x。
數組
如果您知道 awk 可以使用數組,您一定會感到高興。然而,在 awk 中,數組下標通常從 1 開始,而不是 0:
myarray[1]="jim"
myarray[2]=456
awk 遇到第一個賦值語句時,它將創建 myarray,並將元素 myarray[1] 設置成 "jim"。執行了第二個賦值語句後,數組就有兩個元素了。
數組迭代
定義之後,awk 有一個便利的機制來迭代數組元素,如下所示:
for ( x in myarray ) {
print
myarray[x]
}
這段代碼將打印數組 myarray 中的每一個元素。當對於 for 使用這種特殊的 "in" 形式時,awk 將 myarray 的每個現有下標依次賦值給
x(循環控制變量),每次賦值以後都循環一次循環代碼。雖然這是一個非常方便的 awk 功能,但它有一個缺點 -- 當 awk
在數組下標之間輪轉時,它不會依照任何特定的順序。那就意味着我們不能知道以上代碼的輸出是:
jim
456
還是:
456
jim
迭代數組內容就像一盒巧克力-您永遠不知道將會得到什麼。
11、數組下標字符串化
數組下標字符串化
雖然 awk 要執行必要的轉換來完成這項工作,但它卻可以使用某些看起來很奇怪的代碼:
a="1"
b="2"
c=a+b+3
執行了這段代碼後,c 等於 6。由於 awk 是“字符串化”的,添加字符串 "1" 和 "2"
在功能上並不比添加數字 1 和 2 難。這兩種情況下,awk 都可以成功執行運算。awk 的“字符串化”性質非常可愛 --
您可能想要知道如果使用數組的字符串下標會發生什麼情況。例如,以下代碼:
myarr["1"]="Mr. Whipple"
print
myarr["1"]
可以預料,這段代碼將打印 "Mr. Whipple"。但如果去掉第二個 "1" 下標中的引號,情況又會怎樣呢?
myarr["1"]="Mr. Whipple"
print myarr[1]
猜想這個代碼片斷的結果比較難。awk 將 myarr["1"] 和 myarr[1] 看作數組的兩個獨立元素,還是它們是指同一個元素?答案是它們指的是同一個元素,awk 將打印 "Mr. Whipple",如同第一個代碼片斷一樣。雖然看上去可能有點怪,但 awk 在幕後卻一直使用數組的字符串下標!
瞭解了這個奇怪的真相之後,我們中的一些人可能想要執行類似於以下的古怪代碼:
myarr["name"]="Mr.
Whipple"
print myarr["name"]
這段代碼不僅不會產生錯誤,而且它的功能與前面的示例完全相同,也將打印 "Mr. Whipple"!可以看到,awk 並沒有限制我們使用純整數下標;如果我們願意,可以使用字符串下標,而且不會產生任何問題。只要我們使用非整數數組下標,如 myarr["name"],那麼我們就在使用關聯數組。從技術上講,如果我們使用字符串下標,awk 的後臺操作並沒有什麼不同(因爲即便使用“整數”下標,awk 還是會將它看作是字符串)。但是,應該將它們稱作關聯數組 -- 它聽起來很酷,而且會給您的上司留下印象。字符串化下標是我們的小祕密。;)
數組工具
談到數組時,awk 給予我們許多靈活性。可以使用字符串下標,而且不需要連續的數字序列下標(例如,可以定義 myarr[1] 和
myarr[1000],但不定義其它所有元素)。雖然這些都很有用,但在某些情況下,會產生混淆。幸好,awk
提供了一些實用功能有助於使數組變得更易於管理。
首先,可以刪除數組元素。如果想要刪除數組 fooarray 的元素 1,輸入:
delete fooarray[1]
而且,如果想要查看是否存在某個特定數組元素,可以使用特殊的 "in" 布爾運算符,如下所示:
if ( 1 in fooarray )
{
print "Ayep! It's there."
} else {
print "Nope! Can't find
it."
}
12、格式化輸出
awk 提供了兩個函數printf() 和 sprintf()。如同其它許多 awk 部件一樣,這些函數等同於相應的 C
語言函數。
printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。
如果不熟悉
printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本打印函數。在 Linux 系統上,可以輸入 "man 3 printf"
來查看 printf() 幫助頁面。
以下是一些 awk sprintf() 和 printf() 的樣本代碼。可以看到,它們幾乎與 C
語言完全相同。
x=1
b="foo"
printf("%s got a %d on the last
testn","Jim",83)
myout=("%s-%d",b,x)
print myout
此代碼將打印:
Jim got a 83 on the last test
foo-1
13、字符串函數
awk 有許多字符串函數。
在 awk 中,確實需要字符串函數,因爲不能象在其它語言(如 C、C++ 和
Python)中那樣將字符串看作是字符數組。
例如,如果執行以下代碼:
mystring="How are you doing
today?"
print mystring[3]
將會接收到一個錯誤,如下所示:
awk: string.gawk:59: fatal:
attempt to use scalar as array
雖然不象 Python 的序列類型那樣方便,但 awk 的字符串函數還是可以完成任務。讓我們來看一下。
首先,有一個基本 length()
函數,它返回字符串的長度。以下是它的使用方法:
print length(mystring)
此代碼將打印值:
24
下一個字符串函數叫作 index,它將返回子字符串在另一個字符串中出現的位置,如果沒有找到該字符串則返回 0。使用
mystring,可以按以下方法調用它:
print index(mystring,"you")
awk 會打印:
9
兩個簡單的函數,tolower() 和
toupper()。與您猜想的一樣,這兩個函數將返回字符串並且將所有字符分別轉換成小寫或大寫。請注意,tolower() 和 toupper()
返回新的字符串,不會修改原來的字符串。這段代碼:
print tolower(mystring)
print
toupper(mystring)
print mystring
……
將產生以下輸出:
how are you doing
today?
HOW ARE YOU DOING TODAY?
How are you doing today?
到現在爲止一切不錯,但我們究竟如何從字符串中選擇子串,甚至單個字符?那就是使用 substr() 的原因。以下是 substr()
的調用方法:
mysub=substr(mystring,startpos,maxlen)
mystring
應該是要從中抽取子串的字符串變量或文字字符串。startpos 應該設置成起始字符位置,maxlen
應該包含要抽取的字符串的最大長度。請注意,我說的是最大長度;如果 length(mystring) 比 startpos+maxlen
短,那麼得到的結果就會被截斷。substr() 不會修改原始字符串,而是返回子串。以下是一個示例:
print
substr(mystring,9,3)
awk 將打印:
you
如果您通常用於編程的語言使用數組下標訪問部分字符串(以及不使用這種語言的人),請記住 substr() 是 awk
代替方法。
需要使用它來抽取單個字符和子串;因爲 awk 是基於字符串的語言,所以會經常用到它。
一些更耐人尋味的函數
首先是 match()。match() 與 index() 非常相似,它與 index()
的區別在於它並不搜索子串,它搜索的是規則表達式。match() 函數將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match()
還將設置兩個變量,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH
指定它佔據的字符跨度(如果沒有找到匹配,則返回 -1)。通過使用 RSTART、RLENGTH、substr()
和一個小循環,可以輕鬆地迭代字符串中的每個匹配。以下是一個 match() 調用示例:
print match(mystring,/you/),
RSTART, RLENGTH
awk 將打印:
9 9 3
字符串替換
現在,我們將研究兩個字符串替換函數,sub() 和
gsub()。這些函數與目前已經討論過的函數略有不同,因爲它們確實修改原始字符串。以下是一個模板,顯示瞭如何調用
sub():
sub(regexp,replstring,mystring)
調用 sub() 時,它將在 mystring 中匹配 regexp 的第一個字符序列,並且用 replstring 替換該序列。sub() 和
gsub() 用相同的自變量;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub()
將執行全局替換,換出字符串中的所有匹配。以下是一個 sub() 和 gsub() 調用示例:
sub(/o/,"O",mystring)
print
mystring
mystring="How are you doing
today?"
gsub(/o/,"O",mystring)
print mystring
必須將 mystring 復位成其初始值,因爲第一個 sub() 調用直接修改了 mystring。在執行時,此代碼將使 awk 輸出:
HOw
are you doing today?
HOw are yOu dOing tOday?
當然,也可以是更復雜的規則表達式。我把測試一些複雜規則表達式的任務留給您來完成。
通過介紹函數 split(),我們來彙總一下已討論過的函數。split() 的任務是“切開”字符串,並將各部分放到使用整數下標的數組中。以下是一個
split()
調用示例:
numelements=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",mymonths,",")
調用 split() 時,第一個自變量包含要切開文字字符串或字符串變量。在第二個自變量中,應該指定 split()
將填入片段部分的數組名稱。在第三個元素中,指定用於切開字符串的分隔符。split() 返回時,它將返回分割的字符串元素的數量。split()
將每一個片段賦值給下標從 1 開始的數組,因此以下代碼:
print
mymonths[1],mymonths[numelements]
……將打印:
Jan Dec
特殊字符串形式
簡短註釋 -- 調用 length()、sub() 或 gsub() 時,可以去掉最後一個自變量,這樣 awk 將對
$0(整個當前行)應用函數調用。要打印文件中每一行的長度,使用以下 awk 腳本:
{
print length()
}