Awk的一些使用方法

高級shell編程指南:http://www.tsnc.edu.cn/default/tsnc_wgrj/doc/abs-3.9.1_cn/html/awk.html

awk學習筆記:http://man.lupaworld.com/content/manage/ringkee/awk.htm

通用線程:Awk 實例

 

向awk傳遞腳本中的變量加單引號'$pwd'


Daniel Robbins ([email protected])
第一部分
Awk 是一種非常好的語言,同時有一個非常奇怪的名稱。在本系列(共三篇文章)的第一篇文章中,Daniel Robbins 將使您迅速掌握 awk 編程技巧。隨着本系列的進展,將討論更高級的主題,最後將演示一個真正的高級 awk 演示程序。
捍衛 awk
在本系列文章中,我將使您成爲精通 awk 的編碼人員。我承認,awk 並沒有一個非常好聽且又非常“時髦”的名字。awk 的 GNU 版本(叫作 gawk)聽起來非常怪異。那些不熟悉這種語言的人可能聽說過 "awk",並可能認爲它是一組落伍且過時的混亂代碼。它甚至會使最博學的 UNIX 權威陷於錯亂的邊緣(使他不斷地發出 "kill -9!" 命令,就象使用咖啡機一樣)。
的確,awk 沒有一個動聽的名字。但它是一種很棒的語言。awk 適合於文本處理和報表生成,它還有許多精心設計的特性,允許進行需要特殊技巧程序設計。與某些語言不同,awk 的語法較爲常見。它借鑑了某些語言的一些精華部分,如 C 語言、python 和 bash(雖然在技術上,awk 比 python 和 bash 早創建)。awk 是那種一旦學會了就會成爲您戰略編碼庫的主要部分的語言。
第一個 awk
讓我們繼續,開始使用 awk,以瞭解其工作原理。在命令行中輸入以下命令:
$ awk '{ print }' /etc/passwd
您將會見到 /etc/passwd 文件的內容出現在眼前。現在,解釋 awk 做了些什麼。調用 awk 時,我們指定 /etc/passwd 作爲輸入文件。執行 awk 時,它依次對 /etc/passwd 中的每一行執行 print 命令。所有輸出都發送到 stdout,所得到的結果與與執行catting /etc/passwd完全相同。
現在,解釋 { print } 代碼塊。在 awk 中,花括號用於將幾塊代碼組合到一起,這一點類似於 C 語言。在代碼塊中只有一條 print 命令。在 awk 中,如果只出現 print 命令,那麼將打印當前行的全部內容。
這裏是另一個 awk 示例,它的作用與上例完全相同:
$ awk '{ print $0 }' /etc/passwd
在 awk 中,$0 變量表示整個當前行,所以 print 和 print $0 的作用完全一樣。
如果您願意,可以創建一個 awk 程序,讓它輸出與輸入數據完全無關的數據。以下是一個示例:
$ awk '{ print "" }' /etc/passwd
只要將 "" 字符串傳遞給 print 命令,它就會打印空白行。如果測試該腳本,將會發現對於 /etc/passwd 文件中的每一行,awk 都輸出一個空白行。再次說明, awk 對輸入文件中的每一行都執行這個腳本。以下是另一個示例:
$ awk '{ print "hiya" }' /etc/passwd
運行這個腳本將在您的屏幕上寫滿 hiya。:)
多個字段
awk 非常善於處理分成多個邏輯字段的文本,而且讓您可以毫不費力地引用 awk 腳本中每個獨立的字段。以下腳本將打印出您的系統上所有用戶帳戶的列表:

$ awk -F":" '{ print $1 }' /etc/passwd
上例中,在調用 awk 時,使用 -F 選項來指定 ":" 作爲字段分隔符。awk 處理 print $1 命令時,它會打印出在輸入文件中每一行中出現的第一個字段。以下是另一個示例
$ awk -F":" '{ print $1 $3 }' /etc/passwd
以下是該腳本輸出的摘錄
halt7operator11root0shutdown6sync5bin1....etc.
如您所見,awk 打印出 /etc/passwd 文件的第一和第三個字段,它們正好分別是用戶名和用戶標識字段。現在,當腳本運行時,它並不理想 -- 在兩個輸出字段之間沒有空格!如果習慣於使用 bash 或 python 進行編程,那麼您會指望 print $1 $3 命令在兩個字段之間插入空格。然而,當兩個字符串在 awk 程序中彼此相鄰時,awk 會連接它們但不在它們之間添加空格。以下命令會在這兩個字段中插入空格:
$ awk -F":" '{ print $1 " " $3 }' /etc/passwd
以這種方式調用 print 時,它將連接 $1、" " 和 $3,創建可讀的輸出。當然,如果需要的話,我們還可以插入一些文本標籤:
$ awk -F":" '{ print "username: " $1 "/t/tuid:" $3" }' /etc/passwd
這將產生以下輸出:
username: halt     uid:7username: operator   uid:11username: root     uid:0username: shutdown   uid:6username: sync     uid:5username: bin      uid:1....etc.
外部腳本
將腳本作爲命令行自變量傳遞給 awk 對於小的單行程序來說是非常簡單的,而對於多行程序,它就比較複雜。您肯定想要在外部文件中撰寫腳本。然後可以向 awk 傳遞 -f 選項,以向它提供此腳本文件:
$ awk -f myscript.awk myfile.in
將腳本放入文本文件還可以讓您使用附加 awk 功能。例如,這個多行腳本與前面的單行腳本的作用相同,它們都打印出 /etc/passwd 中每一行的第一個字段:
BEGIN {  FS=":"}{ print $1 }
這兩個方法的差別在於如何設置字段分隔符。在這個腳本中,字段分隔符在代碼自身中指定(通過設置 FS 變量),而在前一個示例中,通過在命令行上向 awk 傳遞 -F":" 選項來設置 FS。通常,最好在腳本自身中設置字段分隔符,只是因爲這表示您可以少輸入一個命令行自變量。我們將在本文的後面詳細討論 FS 變量。
BEGIN 和 END 塊
通常,對於每個輸入行,awk 都會執行每個腳本代碼塊一次。然而,在許多編程情況中,可能需要在 awk 開始處理輸入文件中的文本之前執行初始化代碼。對於這種情況,awk 允許您定義一個 BEGIN 塊。我們在前一個示例中使用了 BEGIN 塊。因爲 awk 在開始處理輸入文件之前會執行 BEGIN 塊,因此它是初始化 FS(字段分隔符)變量、打印頁眉或初始化其它在程序中以後會引用的全局變量的極佳位置。
awk 還提供了另一個特殊塊,叫作 END 塊。awk 在處理了輸入文件中的所有行之後執行這個塊。通常,END 塊用於執行最終計算或打印應該出現在輸出流結尾的摘要信息。
規則表達式和塊
awk 允許使用規則表達式,根據規則表達式是否匹配當前行來選擇執行獨立代碼塊。以下示例腳本只輸出包含字符序列 foo 的那些行:
/foo/ { print }
當然,可以使用更復雜的規則表達式。以下腳本將只打印包含浮點數的行:
/[0-9]+/.[0-9]*/ { print }
表達式和塊
還有許多其它方法可以選擇執行代碼塊。我們可以將任意一種布爾表達式放在一個代碼塊之前,以控制何時執行某特定塊。僅當對前面的布爾表達式求值爲真時,awk 才執行代碼塊。以下示例腳本輸出將輸出其第一個字段等於 fred 的所有行中的第三個字段。如果當前行的第一個字段不等於 fred,awk 將繼續處理文件而不對當前行執行 print 語句:
$1 == "fred" { print $3 }
awk 提供了完整的比較運算符集合,包括 "=="、"<"、">"、"<="、">=" 和 "!="。另外,awk 還提供了 "~" 和 "!~" 運算符,它們分別表示“匹配”和“不匹配”。它們的用法是在運算符左邊指定變量,在右邊指定規則表達式。如果某一行的第五個字段包含字符序列 root,那麼以下示例將只打印這一行中的第三個字段:
$5 ~ /root/ { print $3 }
條件語句
awk 還提供了非常好的類似於 C 語言的 if 語句。如果您願意,可以使用 if 語句重寫前一個腳本:
{   if ( $5 ~ /root/ ) {     print $3   }}
這兩個腳本的功能完全一樣。第一個示例中,布爾表達式放在代碼塊外面。而在第二個示例中,將對每一個輸入行執行代碼塊,而且我們使用 if 語句來選擇執行 print 命令。這兩個方法都可以使用,可以選擇最適合腳本其它部分的一種方法。
以下是更復雜的 awk if 語句示例。可以看到,儘管使用了複雜、嵌套的條件語句,if 語句看上去仍與相應的 C 語言 if 語句一樣:
{  if ( $1 == "foo" ) {    if ( $2 == "foo" ) {      print "uno"    } else {      print "one"    }  } else if ($1 == "bar" ) {    print "two"  } else {    print "three"  }}
使用 if 語句還可以將代碼:
! /matchme/ { print $1 $3 $4 }
轉換成:
{    if ( $0 !~ /matchme/ ) {    print $1 $3 $4  }}
這兩個腳本都只輸出不包含 matchme 字符序列的那些行。此外,還可以選擇最適合您的代碼的方法。它們的功能完全相同。
awk 還允許使用布爾運算符 "||"(邏輯與)和 "&&"(邏輯或),以便創建更復雜的布爾表達式:
( $1 == "foo" ) && ( $2 == "bar" ) { print }
這個示例只打印第一個字段等於 foo 且第二個字段等於 bar 的那些行。
數值變量!
至今,我們不是打印字符串、整行就是特定字段。然而,awk 還允許我們執行整數和浮點運算。通過使用數學表達式,可以很方便地編寫計算文件中空白行數量的腳本。以下就是這樣一個腳本:
BEGIN  { x=0 }/^$/  { x=x+1 }END   { print "I found " x " blank lines. :)" }
在 BEGIN 塊中,將整數變量 x 初始化成零。然後,awk 每次遇到空白行時,awk 將執行 x=x+1 語句,遞增 x。處理完所有行之後,執行 END 塊,awk 將打印出最終摘要,指出它找到的空白行數量。
字符串化變量
awk 的優點之一就是“簡單和字符串化”。我認爲 awk 變量“字符串化”是因爲所有 awk 變量在內部都是按字符串形式存儲的。同時,awk 變量是“簡單的”,因爲可以對它執行數學操作,且只要變量包含有效數字字符串,awk 會自動處理字符串到數字的轉換步驟。要理解我的觀點,請研究以下這個示例:
x="1.01"# We just set x to contain the *string* "1.01"x=x+1# We just added one to a *string* print x# Incidentally, these are comments :)
awk 將輸出:
2.01
有趣吧!雖然將字符串值 1.01 賦值給變量 x,我們仍然可以對它加一。但在 bash 和 python 中卻不能這樣做。首先,bash 不支持浮點運算。而且,如果 bash 有“字符串化”變量,它們並不“簡單”;要執行任何數學操作,bash 要求我們將數字放到醜陋的 $( ) ) 結構中。如果使用 python,則必須在對 1.01 字符串執行任何數學運算之前,將它轉換成浮點值。雖然這並不困難,但它仍是附加的步驟。如果使用 awk,它是全自動的,而那會使我們的代碼又好又整潔。如果想要對每個輸入行的第一個字段乘方並加一,可以使用以下腳本:
{ print ($1^2)+1 }
如果做一個小實驗,就可以發現如果某個特定變量不包含有效數字,awk 在對數學表達式求值時會將該變量當作數字零處理。
衆多運算符
awk 的另一個優點是它有完整的數學運算符集合。除了標準的加、減、乘、除,awk 還允許使用前面演示過的指數運算符 "^"、模(餘數)運算符 "%" 和其它許多從 C 語言中借入的易於使用的賦值操作符。
這些運算符包括前後加減(i++、--foo)、加/減/乘/除賦值運算符( a+=3、b*=2、c/=2.2、d-=6.2)。不僅如此 -- 我們還有易於使用的模/指數賦值運算符(a^=2、b%=4)。
字段分隔符
awk 有它自己的特殊變量集合。其中一些允許調整 awk 的運行方式,而其它變量可以被讀取以收集關於輸入的有用信息。我們已經接觸過這些特殊變量中的一個,FS。前面已經提到過,這個變量讓您可以設置 awk 要查找的字段之間的字符序列。我們使用 /etc/passwd 作爲輸入時,將 FS 設置成 ":"。當這樣做有問題時,我們還可以更靈活地使用 FS。
FS 值並沒有被限制爲單一字符;可以通過指定任意長度的字符模式,將它設置成規則表達式。如果正在處理由一個或多個 tab 分隔的字段,您可能希望按以下方式設置 FS:
FS="/t+"
以上示例中,我們使用特殊 "+" 規則表達式字符,它表示“一個或多個前一字符”。
如果字段由空格分隔(一個或多個空格或 tab),您可能想要將 FS 設置成以下規則表達式:
FS="[[:space:]+]"
這個賦值表達式也有問題,它並非必要。爲什麼?因爲缺省情況下,FS 設置成單一空格字符,awk 將這解釋成表示“一個或多個空格或 tab”。在這個特殊示例中,缺省 FS 設置恰恰是您最想要的!
複雜的規則表達式也不成問題。即使您的記錄由單詞 "foo" 分隔,後面跟着三個數字,以下規則表達式仍允許對數據進行正確的分析:
FS="foo[0-9][0-9][0-9]"
字段數量
接着我們要討論的兩個變量通常並不是需要賦值的,而是用來讀取以獲取關於輸入的有用信息。第一個是 NF 變量,也叫做“字段數量”變量。awk 會自動將該變量設置成當前記錄中的字段數量。可以使用 NF 變量來只顯示某些輸入行:
NF == 3 { print "this particular record has three fields: " $0 }
當然,也可以在條件語句中使用 NF 變量,如下:
{    if ( NF > 2 ) {    print $1 " " $2 ":" $3   }}
記錄號
記錄號 (NR) 是另一個方便的變量。它始終包含當前記錄的編號(awk 將第一個記錄算作記錄號 1)。迄今爲止,我們已經處理了每一行包含一個記錄的輸入文件。對於這些情況,NR 還會告訴您當前行號。然而,當我們在本系列以後部分中開始處理多行記錄時,就不會再有這種情況,所以要注意!可以象使用 NF 變量一樣使用 NR 來只打印某些輸入行:
(NR < 10 ) || (NR > 100) { print "We are on record number 1-9 or 101+" }
另一個示例:
{  #skip header  if ( NR > 10 ) {    print "ok, now for the real information!"  }}
awk 提供了適合各種用途的附加變量。我們將在以後的文章中討論這些變量。
現在已經到了初次探索 awk 的尾聲。隨着本系列的開展,我將演示更高級的 awk 功能,我們將用一個真實的 awk 應用程序作爲本系列的結尾。同時,如果急於學習更多知識,請參考以下列出的參考資料。
第二部分
在這篇 awk 簡介的續集中,Daniel Robbins 繼續探索 awk(一種很棒但有怪異名稱的語言)。Daniel 將演示如何處理多行記錄、使用循環結構,以及創建並使用 awk 數組。閱讀完本文後,您將精通許多 awk 的功能,而且可以編寫您自己的功能強大的 awk 腳本。
多行記錄
awk 是一種用於讀取和處理結構化數據(如系統的 /etc/passwd 文件)的極佳工具。/etc/passwd 是 UNIX 用戶數據庫,並且是用冒號定界的文本文件,它包含許多重要信息,包括所有現有用戶帳戶和用戶標識,以及其它信息。在我的前一篇文章中,我演示了 awk 如何輕鬆地分析這個文件。我們只須將 FS(字段分隔符)變量設置成 ":"。
正確設置了 FS 變量之後,就可以將 awk 配置成分析幾乎任何類型的結構化數據,只要這些數據是每行一個記錄。然而,如果要分析佔據多行的記錄,僅僅依靠設置 FS 是不夠的。在這些情況下,我們還需要修改 RS 記錄分隔符變量。RS 變量告訴 awk 當前記錄什麼時候結束,新記錄什麼時候開始。
譬如,讓我們討論一下如何完成處理“聯邦證人保護計劃”所涉及人員的地址列表的任務:
Jimmy the Weasel100 Pleasant DriveSan Francisco, CA 12345Big Tony200 Incognito Ave.Suburbia, WA 67890
理論上,我們希望 awk 將每 3 行看作是一個獨立的記錄,而不是三個獨立的記錄。如果 awk 將地址的第一行看作是第一個字段 ($1),街道地址看作是第二個字段 ($2),城市、州和郵政編碼看作是第三個字段 $3,那麼這個代碼就會變得很簡單。以下就是我們想要得到的代碼:
BEGIN {  FS="/n"  RS=""}
在上面這段代碼中,將 FS 設置成 "/n" 告訴 awk 每個字段都佔據一行。通過將 RS 設置成 "",還會告訴 awk 每個地址記錄都由空白行分隔。一旦 awk 知道是如何格式化輸入的,它就可以爲我們執行所有分析工作,腳本的其餘部分很簡單。讓我們研究一個完整的腳本,它將分析這個地址列表,並將每個記錄打印在一行上,用逗號分隔每個字段。
address.awk
BEGIN {  FS="/n"  RS=""}{  print $1 ", " $2 ", " $3}
如果這個腳本保存爲 address.awk,地址數據存儲在文件 address.txt 中,可以通過輸入 "awk -f address.awk address.txt" 來執行這個腳本。此代碼將產生以下輸出:
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345Big Tony, 200 Incognito Ave., Suburbia, WA 67890
OFS 和 ORS
在 address.awk 的 print 語句中,可以看到 awk 會連接(合併)一行中彼此相鄰的字符串。我們使用此功能在同一行上的三個字段之間插入一個逗號和空格 (", ")。這個方法雖然有用,但比較難看。與其在字段間插入 ", " 字符串,倒不如讓通過設置一個特殊 awk 變量 OFS,讓 awk 完成這件事。請參考下面這個代碼片斷。
print "Hello", "there", "Jim!"
這行代碼中的逗號並不是實際文字字符串的一部分。事實上,它們告訴 awk "Hello"、"there" 和 "Jim!" 是單獨的字段,並且應該在每個字符串之間打印 OFS 變量。缺省情況下,awk 產生以下輸出:
Hello there Jim!
這是缺省情況下的輸出結果,OFS 被設置成 " ",單個空格。不過,我們可以方便地重新定義 OFS,這樣 awk 將插入我們中意的字段分隔符。以下是原始 address.awk 程序的修訂版,它使用 OFS 來輸出那些中間的 ", " 字符串:
address.awk 的修訂版
BEGIN {  FS="/n"  RS=""  OFS=", "}{  print $1, $2, $3}
awk 還有一個特殊變量 ORS,全稱是“輸出記錄分隔符”。通過設置缺省爲換行 ("/n") 的 OFS,我們可以控制在 print 語句結尾自動打印的字符。缺省 ORS 值會使 awk 在新行中輸出每個新的 print 語句。如果想使輸出的間隔翻倍,可以將 ORS 設置成 "/n/n"。或者,如果想要用單個空格分隔記錄(而不換行),將 ORS 設置成 ""。
將多行轉換成用 tab 分隔的格式
假設我們編寫了一個腳本,它將地址列表轉換成每個記錄一行,且用 tab 定界的格式,以便導入電子表格。使用稍加修改的 address.awk 之後,就可以清楚地看到這個程序只適合於三行的地址。如果 awk 遇到以下地址,將丟掉第四行,並且不打印該行:
Cousin VinnieVinnie's Auto Shop300 City AlleySosueme, OR 76543
要處理這種情況,代碼最好考慮每個字段的記錄數量,並依次打印每個記錄。現在,代碼只打印地址的前三個字段。以下就是我們想要的一些代碼:
適合具有任意多字段的地址的 address.awk 版本
BEGIN {   FS="/n"   RS=""   ORS="" } {     x=1     while ( x<NF ) {         print $x "/t"         x++     }     print $NF "/n" }
首先,將字段分隔符 FS 設置成 "/n",將記錄分隔符 RS 設置成 "",這樣 awk 可以象以前一樣正確分析多行地址。然後,將輸出記錄分隔符 ORS 設置成 "",它將使 print 語句在每個調用結尾不輸出新行。這意味着如果希望任何文本從新的一行開始,那麼需要明確寫入 print "/n"。
在主代碼塊中,創建了一個變量 x 來存儲正在處理的當前字段的編號。起初,它被設置成 1。然後,我們使用 while 循環(一種 awk 循環結構,等同於 C 語言中的 while 循環),對於所有記錄(最後一個記錄除外)重複打印記錄和 tab 字符。最後,打印最後一個記錄和換行;此外,由於將 ORS 設置成 "",print 將不輸出換行。程序輸出如下,這正是我們所期望的:
我們想要的輸出。不算漂亮,但用 tab 定界,以便於導入電子表格
Jimmy the Weasel    100 Pleasant Drive   San Francisco, CA 12345 Big Tony    200 Incognito Ave.   Suburbia, WA 67890Cousin Vinnie  Vinnie's Auto Shop   300 City Alley Sosueme, OR 76543
循環結構
我們已經看到了 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 1iteration 2iteration 3iteration 4
break 和 continue
此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的循環結構。以下是迫切需要 break 語句的代碼片斷:
while 死循環
while (1) {  print "forever and ever..."}
因爲 1 永遠代表是真,這個 while 循環將永遠運行下去。以下是一個只執行十次的循環:
break 語句示例
x=1while(1) {  print "iteration",x  if ( x == 10 ) {    break  }  x++}
這裏,break 語句用於“逃出”最深層的循環。"break" 使循環立即終止,並繼續執行循環代碼塊後面的語句。
continue 語句補充了 break,其作用如下:
x=1while (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 在數組下標之間輪轉時,它不會依照任何特定的順序。那就意味着我們不能知道以上代碼的輸出是
jim456
還是
456jim
套用 Forrest Gump 的話來說,迭代數組內容就像一盒巧克力 -- 您永遠不知道將會得到什麼。因此有必要使 awk 數組“字符串化”,我們現在就來研究這個問題。
數組下標字符串化
在我的前一篇文章中,我演示了 awk 實際上以字符串格式來存儲數字值。雖然 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."}
下一篇
本文中,我們已經討論了許多基礎知識。下一篇中,我將演示如何使用 awk 的數學運算和字符串函數,以及如何創建您自己的函數,使您完全掌握 awk 知識。我還將指導您創建支票簿結算程序。那時,我會鼓勵您編寫自己的 awk 程序。請查閱以下參考資料。
在 awk 系列的這篇總結中,Daniel 向您介紹 awk 重要的字符串函數,以及演示瞭如何從頭開始編寫完整的支票簿結算程序。在這個過程中,您將學習如何編寫自己的函數,並使用 awk 的多維數組。學完本文之後,您將掌握更多 awk 經驗,可以讓您創建功能更強大的腳本。
第三部分
格式化輸出
雖然大多數情況下 awk 的 print 語句可以完成任務,但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函數等同於相應的 C 語言函數。printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本打印函數。在 Linux 系統上,可以輸入 "man 3 printf" 來查看 printf() 幫助頁面。
以下是一些 awk sprintf() 和 printf() 的樣本代碼。可以看到,它們幾乎與 C 語言完全相同。
x=1b="foo"printf("%s got a %d on the last test/n","Jim",83)myout=("%s-%d",b,x)print myout
此代碼將打印:
Jim got a 83 on the last testfoo-1
字符串函數
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 mystringmystring="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() }
財務上的趣事
幾星期前,我決定用 awk 編寫自己的支票簿結算程序。我決定使用簡單的 tab 定界文本文件,以便於輸入最近的存款和提款記錄。其思路是將這個數據交給 awk 腳本,該腳本會自動合計所有金額,並告訴我餘額。以下是我決定如何將所有交易記錄到 "ASCII checkbook" 中:
23 Aug 2000 food  -  -  Y  Jimmy's Buffet   30.25
此文件中的每個字段都由一個或多個 tab 分隔。在日期(字段 1,$1)之後,有兩個字段叫做“費用分類帳”和“收入分類帳”。以上面這行爲例,輸入費用時,我在費用字段中放入四個字母的別名,在收入字段中放入 "-"(空白項)。這表示這一特定項是“食品費用”。:) 以下是存款的示例:
23 Aug 2000 -  inco  -  Y  Boss Man    2001.00
在這個實例中,我在費用分類帳中放入 "-"(空白),在收入分類帳中放入 "inco"。"inco" 是一般(薪水之類)收入的別名。使用分類帳別名讓我可以按類別生成收入和費用的明細分類帳。至於記錄的其餘部分,其它所有字段都是不需加以說明的。“是否付清?”字段("Y" 或 "N")記錄了交易是否已過帳到我的帳戶;除此之外,還有一個交易描述,和一個正的美元金額。
用於計算當前餘額的算法不太難。awk 只需要依次讀取每一行。如果列出了費用分類帳,但沒有收入分類帳(爲 "-"),那麼這一項就是借方。如果列出了收入分類帳,但沒有費用分類帳(爲 "-"),那麼這一項就是貸方。而且,如果同時列出了費用和收入分類帳,那麼這個金額就是“分類帳轉帳”;即,從費用分類帳減去美元金額,並將此金額添加到收入分類帳。此外,所有這些分類帳都是虛擬的,但對於跟蹤收入和支出以及預算卻非常有用。
代碼
現在該研究代碼了。我們將從第一行(BEGIN 塊和函數定義)開始:
balance,第 1 部分
#!/usr/bin/env awk -fBEGIN {   FS="/t+"  months="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"}function monthdigit(mymonth) {  return (index(months,mymonth)+3)/4}
首先執行 "chmod +x myscript" 命令,那麼將第一行 "#!..." 添加到任何 awk 腳本將使它可以直接從 shell 中執行。其餘行定義了 BEGIN 塊,在 awk 開始處理支票簿文件之前將執行這個代碼塊。我們將 FS(字段分隔符)設置成 "/t+",它會告訴 awk 字段由一個或多個 tab 分隔。另外,我們定義了字符串 months,下面將出現的 monthdigit() 函數將使用它。
最後三行顯示瞭如何定義自己的 awk 。格式很簡單 -- 輸入 "function",再輸入名稱,然後在括號中輸入由逗號分隔的參數。在此之後,"{ }" 代碼塊包含了您希望這個函數執行的代碼。所有函數都可以訪問全局變量(如 months 變量)。另外,awk 提供了 "return" 語句,它允許函數返回一個值,並執行類似於 C 和其它語言中 "return" 的操作。這個特定函數將以 3 個字母字符串格式表示的月份名稱轉換成等價的數值。例如,以下代碼:
print monthdigit("Mar")
……將打印:
3
現在,讓我們討論其它一些函數。
財務函數
以下是其它三個執行簿記的函數。我們即將見到的主代碼塊將調用這些函數之一,按順序處理支票簿文件的每一行,從而將相應交易記錄到 awk 數組中。有三種基本交易,貸方 (doincome)、借方 (doexpense) 和轉帳 (dotransfer)。您會發現這三個函數全都接受一個自變量,叫作 mybalance。mybalance 是二維數組的一個佔位符,我們將它作爲自變量進行傳遞。目前,我們還沒有處理過二維數組;但是,在下面可以看到,語法非常簡單。只須用逗號分隔每一維就行了。
我們將按以下方式將信息記錄到 "mybalance" 中。數組的第一維從 0 到 12,用於指定月份,0 代表全年。第二維是四個字母的分類帳,如 "food" 或 "inco";這是我們處理的真實分類帳。因此,要查找全年食品分類帳的餘額,應查看 mybalance[0,"food"]。要查找 6 月的收入,應查看 mybalance[6,"inco"]。
balance,第 2 部分
function doincome(mybalance) {  mybalance[curmonth,$3] += amount  mybalance[0,$3] += amount}function doexpense(mybalance) {  mybalance[curmonth,$2] -= amount  mybalance[0,$2] -= amount}function dotransfer(mybalance) {  mybalance[0,$2] -= amount  mybalance[curmonth,$2] -= amount  mybalance[0,$3] += amount  mybalance[curmonth,$3] += amount}
調用 doincome() 或任何其它函數時,我們將交易記錄到兩個位置 -- mybalance[0,category] 和 mybalance[curmonth, category],它們分別表示全年的分類帳餘額和當月的分類帳餘額。這讓我們稍後可以輕鬆地生成年度或月度收入/支出明細分類帳。
如果研究這些函數,將發現在我的引用中傳遞了 mybalance 引用的數組。另外,我們還引用了幾個全局變量:curmonth,它保存了當前記錄所屬的月份的數值,$2(費用分類帳),$3(收入分類帳)和金額($7,美元金額)。調用 doincome() 和其它函數時,已經爲要處理的當前記錄(行)正確設置了所有這些變量。
主塊
以下是主代碼塊,它包含了分析每一行輸入數據的代碼。請記住,由於正確設置了 FS,可以用 $ 1 引用第一個字段,用 $2 引用第二個字段,依次類推。調用 doincome() 和其它函數時,這些函數可以從函數內部訪問 curmonth、$2、$3 和金額的當前值。請先研究代碼,在代碼之後可以見到我的說明。
balance,第 3 部分
{  curmonth=monthdigit(substr($1,4,3))  amount=$7    #record all the categories encountered  if ( $2 != "-" )    globcat[$2]="yes"  if ( $3 != "-" )    globcat[$3]="yes"  #tally up the transaction properly  if ( $2 == "-" ) {    if ( $3 == "-" ) {      print "Error: inc and exp fields are both blank!"      exit 1    } else {      #this is income      doincome(balance)      if ( $5 == "Y" )        doincome(balance2)    }  } else if ( $3 == "-" ) {    #this is an expense     doexpense(balance)    if ( $5 == "Y" )       doexpense(balance2)  } else {    #this is a transfer    dotransfer(balance)    if ( $5 == "Y" )       dotransfer(balance2)  }      }
在主塊中,前兩行將 curmonth 設置成 1 到 12 之間的整數,並將金額設置成字段 7(使代碼易於理解)。然後,是四行有趣的代碼,它們將值寫到數組 globcat 中。globcat,或稱作全局分類帳數組,用於記錄在文件中遇到的所有分類帳 -- "inco"、"misc"、"food"、"util" 等。例如,如果 $2 == "inco",則將 globcat["inco"] 設置成 "yes"。稍後,我們可以使用簡單的 "for (x in globcat)" 循環來迭代分類帳列表。
在接着的大約二十行中,我們分析字段 $2 和 $3,並適當記錄交易。如果 $2=="-" 且 $3!="-",表示我們有收入,因此調用 doincome()。如果是相反的情況,則調用 doexpense();如果 $2 和 $3 都包含分類帳,則調用 dotransfer()。每次我們都將 "balance" 數組傳遞給這些函數,從而在這些函數中記錄適當的數據。
您還會發現幾行代碼說“if ( $5 == "Y" ),那麼將同一個交易記錄到 balance2 中”。我們在這裏究竟做了些什麼?您將回憶起 $5 包含 "Y" 或 "N",並記錄交易是否已經過帳到帳戶。由於僅當過帳了交易時我們纔將交易記錄到 balance2,因此 balance2 包含了真實的帳戶餘額,而 "balance" 包含了所有交易,不管是否已經過帳。可以使用 balance2 來驗證數據項(因爲它應該與當前銀行帳戶餘額匹配),可以使用 "balance" 來確保沒有透支帳戶(因爲它會考慮您開出的尚未兌現的所有支票)。
生成報表
主塊重複處理了每一行記錄之後,現在我們有了關於比較全面的、按分類帳和按月份劃分的借方和貸方記錄。現在,在這種情況下最合適的做法是隻須定義生成報表的 END 塊:
balance,第 4 部分
END {  bal=0  bal2=0   for (x in globcat) {    bal=bal+balance[0,x]    bal2=bal2+balance2[0,x]  }  printf("Your available funds: %10.2f/n", bal)  printf("Your account balance: %10.2f/n", bal2) }
這個報表將打印出彙總,如下所示:
Your available funds:1174.22Your account balance:2399.33
在 END 塊中,我們使用 "for (x in globcat)" 結構來迭代每一個分類帳,根據記錄在案的交易結算主要餘額。實際上,我們結算兩個餘額,一個是可用資金,另一個是帳戶餘額。要執行程序並處理您在文件 "mycheckbook.txt" 中輸入的財務數據,將以上所有代碼放入文本文件 "balance",執行 "chmod +x balance",然後輸入 "./balance mycheckbook.txt"。然後 balance 腳本將合計所有交易,打印出兩行餘額彙總。
升級
我使用這個程序的更高級版本來管理我的個人和企業財務。我的版本(由於篇幅限制不能在此涵蓋)會打印出收入和費用的月度明細分類帳,包括年度總合、淨收入和其它許多內容。它甚至以 HTML 格式輸出數據,因此我可以在 Web 瀏覽器中查看它。:) 如果您認爲這個程序有用,我建議您將這些特性添加到這個腳本中。不必將它配置成要 記錄任何附加信息;所需的全部信息已經在 balance 和 balance2 裏面了。只要升級 END 塊就萬事具備了!
我希望您喜歡本系列。有關 awk 的詳細信息,請參考以下列出的參考資料。

參考資料
·如果想看好的老式書籍,O'Reilly 的 sed & awk, 2ndEdition 是極佳選擇。
·請參考 comp.lang.awkFAQ。它還包含許多附加 awk 鏈接。
·Patrick Hartigan 的 awk tutorial 還包括了實用的 awk 腳本。
·Thompson's TAWKCompiler 將 awk 腳本編譯成快速二進制可執行文件。可用版本有 Windows 版、OS/2 版、DOS 版和 UNIX 版。
·The GNUAwk User's Guide 可用於在線參考。
關於作者
Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的總裁兼 CEO,Gentoo Linux(用於 PC 的高級 Linux)和 Portage 系統(Linux 的下一代移植系統)的創始人。他還是 Macmillan 書籍 Caldera OpenLinux Unleashed、SuSE Linux Unleashed 和 Samba Unleashed 的合作者。Daniel 自二年級起就與計算機結下不解之緣,那時他首先接觸的是 Logo 程序語言,並沉溺於 Pac-Man 遊戲中。這也許就是他至今仍擔任 SONY Electronic Publishing/Psygnosis 的首席圖形設計師的原因所在。Daniel 喜歡與妻子 Mary 和新出生的女兒 Hadassah 一起共度時光。可通過 [email protected] 與 Daniel 聯繫。

一個例子:
有工資文件gz2.txt如下:
姓名  賬號        金額
-----------------------------------
張三  43674229263501331001250
李四  43674229263512201781300
王二  43674229263512205460
蘇五丙           1340
孫六月 4367422926351220178390
…… ……
這個工資文件相比以上要特殊一些,首先人名在前,而且賬號與金額聯在一起,19位賬號就是金額。
要求:按賬號19位、姓名8位、工資8位來排列,且如姓名不足8位在之後補足,工資不足8位則在工資之前補。同時要求去掉前面兩行及工資爲0的名單,沒有賬號在前補19位空格,並輸出工資總數加以覈對,處理後應如下排列:
4367422926350133100張三   1250.00
4367422926351220178李四   1300.00
         蘇五丙  1340.00
4367422926351220178孫六月  390.00
…… ……
awk程序:
--------------------------------
#shgz2.sh
cut -c1-8 $1>tmp1.txt   #用cut命令分別提出三個字段的內容。
cut -c9-27 $1>tmp2.txt
cut -c28-60 $1>tmp3.txt
paste -d, tmp2.txt tmp1.txt tmp3.txt|tr -d " ">tmp4.txt #三個文件合一,並用”,”爲分隔符
awk -F, '{          #-F, 表示分隔符爲”,”
if ($1~/^2/)
  printf("%-19.19s%-8.8s%8.2f/n",$1,$2,$3)
else
 printf("%-19.19s%-8.8s%8.2f/n","'"$kk"'",$2,$3)}' tmp4.txt > tmp5.txt
sed '/ 0.00$/d' tmp5.txt>$2  #去掉金額爲0的行
awk '$NF~/[0-9]/{
sum=sum+$NF
}
END{
system("rm tmp*.txt")               #刪除臨時生成的文件
printf("The sum is%16.2f!/n", sum)        #輸出工資總數
}' $2
-----------------------------------------------
附註:
 本例中結合了cut,sed與awk的用法!相關命令及參數可參考相關書籍如cut -c1-8 表示提出每一行的1到8位字符. 
 與awk一樣,cut也可以按分隔符來分離字段,而且缺省的分隔符爲空格,當然也可修改.cut -f1 gz2.txt就會取出姓名這個字段.但是我們可以看到,賬號與金額是分不出來的,所以也是我們用cut -c1-8的原因.



什麼是awk?
你可能對UNIX比較熟悉,但你可能對awk很陌生,這一點也不奇怪,的確,與其優秀的功能相比,awk還遠沒達到它應有的知名度。awk是什麼?與其它大多數UNIX命令不同的是,從名字上看,我們不可能知道awk的功能:它既不是具有獨立意義的英文單詞,也不是幾個相關單詞的縮寫。事實上,awk是三個人名的縮寫,他們是:Aho、(Peter)Weinberg和(Brain)Kernighan。正是這三個人創造了awk---一個優秀的樣式掃描與處理工具。

AWK的功能是什麼?與sed和grep很相似,awk是一種樣式掃描與處理工具。但其功能卻大大強於sed和grep。awk提供了極其強大的功能:它幾乎可以完成grep和sed所能完成的全部工作,同時,它還可以可以進行樣式裝入、流控制、數學運算符、進程控制語句甚至於內置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上,awk的確擁有自己的語言:awk程序設計語言,awk的三位創建者已將它正式定義爲:樣式掃描和處理語言。

爲什麼使用awk?

即使如此,你也許仍然會問,我爲什麼要使用awk?

使用awk的第一個理由是基於文本的樣式掃描和處理是我們經常做的工作,awk所做的工作有些象數據庫,但與數據庫不同的是,它處理的是文本文件,這些文件沒有專門的存儲格式,普通的人們就能編輯、閱讀、理解和處理它們。而數據庫文件往往具有特殊的存儲格式,這使得它們必須用數據庫處理程序來處理它們。既然這種類似於數據庫的處理工作我們經常會遇到,我們就應當找到處理它們的簡便易行的方法,UNIX有很多這方面的工具,例如sed 、grep、sort以及find等等,awk是其中十分優秀的一種。

使用awk的第二個理由是awk是一個簡單的工具,當然這是相對於其強大的功能來說的。的確,UNIX有許多優秀的工具,例如UNIX天然的開發工具C語言及其延續C++就非常的優秀。但相對於它們來說,awk完成同樣的功能要方便和簡捷得多。這首先是因爲awk提供了適應多種需要的解決方案:從解決簡單問題的awk命令行到複雜而精巧的awk程序設計語言,這樣做的好處是,你可以不必用複雜的方法去解決本來很簡單的問題。例如,你可以用一個命令行解決簡單的問題,而C不行,即使一個再簡單的程序,C語言也必須經過編寫、編譯的全過程。其次,awk本身是解釋執行的,這就使得awk程序不必經過編譯的過程,同時,這也使得它與shell script程序能夠很好的契合。最後,awk本身較C語言簡單,雖然awk吸收了C語言很多優秀的成分,熟悉C語言會對學習awk有很大的幫助,但awk本身不須要會使用C語言——一種功能強大但需要大量時間學習才能掌握其技巧的開發工具。

使用awk的第三個理由是awk是一個容易獲得的工具。與C和C++語言不同,awk只有一個文件(/bin/awk),而且幾乎每個版本的UNIX都提供各自版本的awk,你完全不必費心去想如何獲得awk。但C語言卻不是這樣,雖然C語言是UNIX天然的開發工具,但這個開發工具卻是單獨發行的,換言之,你必須爲你的UNIX版本的C語言開發工具單獨付費(當然使用D版者除外),獲得並安裝它,然後你纔可以使用它。

基於以上理由,再加上awk強大的功能,我們有理由說,如果你要處理與文本樣式掃描相關的工作,awk應該是你的第一選擇。在這裏有一個可遵循的一般原則:如果你用普通的shell工具或shell script有困難的話,試試awk,如果awk仍不能解決問題,則便用C語言,如果C語言仍然失敗,則移至C++。

awk的調用方式

前面曾經說過,awk提供了適應多種需要的不同解決方案,它們是:

一、awk命令行,你可以象使用普通UNIX命令一樣使用awk,在命令行中你也可以使用awk程序設計語言,雖然awk支持多行的錄入,但是錄入長長的命令行並保證其正確無誤卻是一件令人頭疼的事,因此,這種方法一般只用於解決簡單的問題。當然,你也可以在shell script程序中引用awk命令行甚至awk程序腳本。

二、使用-f選項調用awk程序。awk允許將一段awk程序寫入一個文本文件,然後在awk命令行中用-f選項調用並執行這段程序。具體的方法我們將在後面的awk語法中講到。

三、利用命令解釋器調用awk程序:利用UNIX支持的命令解釋器功能,我們可以將一段awk程序寫入文本文件,然後在它的第一行加上:
#!/bin/awk -f
並賦予這個文本文件以執行的權限。這樣做之後,你就可以在命令行中用類似於下面這樣的方式調用並執行這段awk程序了。

$awk腳本文本名 待處理文件

awk的語法:

與其它UNIX命令一樣,awk擁有自己的語法:

awk [ -F re] [parameter...] ['prog'] [-f progfile][in_file...]

參數說明:

-F re:允許awk更改其字段分隔符。

parameter: 該參數幫助爲不同的變量賦值。

'prog': awk的程序語句段。這個語句段必須用單拓號:'和'括起,以防被shell解釋。這個程序語句段的標準形式爲:

'pattern {action}'

其中pattern參數可以是egrep正則表達式中的任何一個,它可以使用語法/re/再加上一些樣式匹配技巧構成。與sed類似,你也可以使用","分開兩樣式以選擇某個範圍。關於匹配的細節,你可以參考附錄,如果仍不懂的話,找本UNIX書學學grep和sed(本人是在學習ed時掌握匹配技術的)。action參數總是被大括號包圍,它由一系統awk語句組成,各語句之間用";"分隔。awk解釋它們,並在pattern給定的樣式匹配的記錄上執行其操作。與shell類似,你也可以使用“#”作爲註釋符,它使“#”到行尾的內容成爲註釋,在解釋執行時,它們將被忽略。你可以省略pattern和action之一,但不能兩者同時省略,當省略pattern時沒有樣式匹配,表示對所有行(記錄)均執行操作,省略action時執行缺省的操作——在標準輸出上顯示。

-f progfile:允許awk調用並執行progfile指定有程序文件。progfile是一個文本文件,他必須符合awk的語法。

in_file:awk的輸入文件,awk允許對多個輸入文件進行處理。值得注意的是awk不修改輸入文件。如果未指定輸入文件,awk將接受標準輸入,並將結果顯示在標準輸出上。awk支持輸入輸出重定向。

awk的記錄、字段與內置變量:

前面說過,awk處理的工作與數據庫的處理方式有相同之處,其相同處之一就是awk支持對記錄和字段的處理,其中對字段的處理是grep和sed不能實現的,這也是awk優於二者的原因之一。在awk中,缺省的情況下總是將文本文件中的一行視爲一個記錄,而將一行中的某一部分作爲記錄中的一個字段。爲了操作這些不同的字段,awk借用shell的方法,用$1,$2,$3...這樣的方式來順序地表示行(記錄)中的不同字段。特殊地,awk用$0表示整個行(記錄)。不同的字段之間是用稱作分隔符的字符分隔開的。系統默認的分隔符是空格。awk允許在命令行中用-F re的形式來改變這個分隔符。事實上,awk用一個內置的變量FS來記憶這個分隔符。awk中有好幾個這樣的內置變量,例如,記錄分隔符變量RS、當前工作的記錄數NR等等,本文後面的附表列出了全部的內置變量。這些內置的變量可以在awk程序中引用或修改,例如,你可以利用NR變量在模式匹配中指定工作範圍,也可以通過修改記錄分隔符RS讓一個特殊字符而不是換行符作爲記錄的分隔符。

例:顯示文本文件myfile中第七行到第十五行中以字符%分隔的第一字段,第三字段和第七字段:

awk -F % 'NR==7,NR==15 {printf $1 $3 $7}'

awk的內置函數

awk之所以成爲一種優秀的程序設計語言的原因之一是它吸收了某些優秀的程序設計語言(例如C)語言的許多優點。這些優點之一就是內置函數的使用,awk定義並支持了一系列的內置函數,由於這些函數的使用,使得awk提供的功能更爲完善和強大,例如,awk使用了一系列的字符串處理內置函數(這些函數看起來與C語言的字符串處理函數相似,其使用方式與C語言中的函數也相差無幾),正是由於這些內置函數的使用,使awk處理字符串的功能更加強大。本文後面的附錄中列有一般的awk所提供的內置函數,這些內置函數也許與你的awk版本有些出入,因此,在使用之前,最好參考一下你的系統中的聯機幫助。

作爲內置函數的一個例子,我們將在這裏介紹awk的printf函數,這個函數使得awk與c語言的輸出相一致。實際上,awk中有許多引用形式都是從C語言借用過來的。如果你熟悉C語言,你也許會記得其中的printf函數,它提供的強大格式輸出功能曾經帶我們許多的方便。幸運的是,我們在awk中又和它重逢了。awk中printf幾乎與C語言中一模一樣,如果你熟悉C語言的話,你完全可以照C語言的模式使用awk中的printf。因此在這裏,我們只給出一個例子,如果你不熟悉的話,請隨便找一本C語言的入門書翻翻。

例:顯示文件myfile中的行號和第3字段:

$awk '{printf"%03d%s/n",NR,$1}' myfile

在命令行使用awk

按照順序,我們應當講解awk程序設計的內容了,但在講解之前,我們將用一些例子來對前面的知識進行回顧,這些例子都是在命令行中使用的,由此我們可以知道在命令行中使用awk是多麼的方便。這樣做的原因一方面是爲下面的內容作鋪墊,另一方面是介紹一些解決簡單問題的方法,我們完全沒有必要用複雜的方法來解決簡單的問題----既然awk提供了較爲簡單的方法的話。

例:顯示文本文件mydoc匹配(含有)字符串"sun"的所有行。

$awk '/sun/{print}' mydoc

由於顯示整個記錄(全行)是awk的缺省動作,因此可以省略action項。

$awk '/sun/' mydoc

例:下面是一個較爲複雜的匹配的示例:

$awk '/[Ss]un/,/[Mm]oon/ {print}' myfile

它將顯示第一個匹配Sun或sun的行與第一個匹配Moon或moon的行之間的行,並顯示到標準輸出上。

例:下面的示例顯示了內置變量和內置函數length()的使用:

$awk 'length($0)>80 {print NR}' myfile

該命令行將顯示文本myfile中所有超過80個字符的行號,在這裏,用$0表示整個記錄(行),同時,內置變量NR不使用標誌符'$'。

例:作爲一個較爲實際的例子,我們假設要對UNIX中的用戶進行安全性檢查,方法是考察/etc下的passwd文件,檢查其中的passwd字段(第二字段)是否爲"*",如不爲"*",則表示該用戶沒有設置密碼,顯示出這些用戶名(第一字段)。我們可以用如下語句實現:

#awk -F: '$2=="" {printf("%s no password!/n",$1' /etc/passwd

在這個示例中,passwd文件的字段分隔符是“:”,因此,必須用-F:來更改默認的字段分隔符,這個示例中也涉及到了內置函數printf的使用。

awk的變量

如同其它程序設計語言一樣,awk允許在程序語言中設置變量,事實上,提供變量的功能是程序設計語言的其本要求,不提供變量的程序設計語言本人還從未見過。

awk提供兩種變量,一種是awk內置的變量,這前面我們已經講過,需要着重指出的是,與後面提到的其它變量不同的是,在awk程序中引用內置變量不需要使用標誌符"$"(回憶一下前面講過的NR的使用)。awk提供的另一種變量是自定義變量。awk允許用戶在awk程序語句中定義並調用自已的變量。當然這種變量不能與內置變量及其它awk保留字相同,在awk中引用自定義變量必須在它前面加上標誌符"$"。與C語言不同的是,awk中不需要對變量進行初始化,awk根據其在awk中第一次出現的形式和上下文確定其具體的數據類型。當變量類型不確定時,awk默認其爲字符串類型。這裏有一個技巧:如果你要讓你的awk程序知道你所使用的變量的明確類型,你應當在在程序中給它賦初值。在後面的實例中,我們將用到這一技巧。

運算與判斷:

作爲一種程序設計語言所應具有的特點之一,awk支持多種運算,這些運算與C語言提供的幾本相同:如+、-、*、/、%等等,同時,awk也支持C語言中類似++、--、+=、-=、=+、=-之類的功能,這給熟悉C語言的使用者編寫awk程序帶來了極大的方便。作爲對運算功能的一種擴展,awk還提供了一系列內置的運算函數(如log、sqr、cos、sin等等)和一些用於對字符串進行操作(運算)的函數(如length、substr等等)。這些函數的引用大大的提高了awk的運算功能。

作爲對條件轉移指令的一部分,關係判斷是每種程序設計語言都具備的功能,awk也不例外。awk中允許進行多種測試,如常用的==(等於)、!=(不等於)、>(大於)、<(小於)、>=(大於等於)、>=(小於等於)等等,同時,作爲樣式匹配,還提供了~(匹配於)和!~(不匹配於)判斷。

作爲對測試的一種擴充,awk也支持用邏輯運算符:!(非)、&&(與)、||(或)和括號()進行多重判斷,這大大增強了awk的功能。本文的附錄中列出了awk所允許的運算、判斷以及操作符的優先級。

awk的流程控制

流程控制語句是任何程序設計語言都不能缺少的部分。任何好的語言都有一些執行流程控制的語句。awk提供的完備的流程控制語句類似於C語言,這給我們編程帶來了極大的方便。

1、BEGIN和END:

在awk中兩個特別的表達式,BEGIN和END,這兩者都可用於pattern中(參考前面的awk語法),提供BEGIN和END的作用是給程序賦予初始狀態和在程序結束之後執行一些掃尾的工作。任何在BEGIN之後列出的操作(在{}內)將在awk開始掃描輸入之前執行,而END之後列出的操作將在掃描完全部的輸入之後執行。因此,通常使用BEGIN來顯示變量和預置(初始化)變量,使用END來輸出最終結果。

例:累計銷售文件xs中的銷售金額(假設銷售金額在記錄的第三字段):

$awk
>'BEGIN { FS=":";print "統計銷售金額";total=0}
>{print $3;total=total+$3;}
>END {printf "銷售金額總計:%.2f",total}' sx
(注:>是shell提供的第二提示符,如要在shell程序awk語句和awk語言中換行,則需在行尾加反斜槓/)

在這裏,BEGIN預置了內部變量FS(字段分隔符)和自定義變量total,同時在掃描之前顯示出輸出行頭。而END則在掃描完成後打印出總合計。

2、流程控制語句
awk提供了完備的流程控制語句,其用法與C語言類似。下面我們一一加以說明:

2.1、if...else語句:

格式:
if(表達式)
語句1
else
語句2

格式中"語句1"可以是多個語句,如果你爲了方便awk判斷也方便你自已閱讀,你最好將多個語句用{}括起來。awk分枝結構允許嵌套,其格式爲:

if(表達式1)
{if(表達式2)
語句1
else
語句2
}
語句3
else {if(表達式3)
語句4
else
語句5
}
語句6

當然實際操作過程中你可能不會用到如此複雜的分枝結構,這裏只是爲了給出其樣式罷了。

2.2、while語句

格式爲:

while(表達式)
語句

2.3、do-while語句

格式爲:

do
{
語句
}while(條件判斷語句)

2.4、for語句

格式爲:

for(初始表達式;終止條件;步長表達式)
{語句}

在awk的 while、do-while和for語句中允許使用break,continue語句來控制流程走向,也允許使用exit這樣的語句來退出。break中斷當前正在執行的循環並跳到循環外執行下一條語句。continue從當前位置跳到循環開始處執行。對於exit的執行有兩種情況:當exit語句不在END中時,任何操作中的exit命令表現得如同到了文件尾,所有模式或操作執行將停止,END模式中的操作被執行。而出現在END中的exit將導致程序終止。

例:爲了

awk中的自定義函數

定義和調用用戶自己的函數是幾乎每個高級語言都具有的功能,awk也不例外,但原始的awk並不提供函數功能,只有在nawk或較新的awk版本中才可以增加函數。

函數的使用包含兩部分:函數的定義與函數調用。其中函數定義又包括要執行的代碼(函數本身)和從主程序代碼傳遞到該函數的臨時調用。

awk函數的定義方法如下:

function 函數名(參數表){
函數體
}

在gawk中允許將function省略爲func,但其它版本的awk不允許。函數名必須是一個合法的標誌符,參數表中可以不提供參數(但在調用函數時函數名後的一對括號仍然是不可缺少的),也可以提供一個或多個參數。與C語言相似,awk的參數也是通過值來傳遞的。

在awk中調用函數比較簡單,其方法與C語言相似,但awk比C語言更爲靈活,它不執行參數有效性檢查。換句話說,在你調用函數時,可以列出比函數預計(函數定義中規定)的多或少的參數,多餘的參數會被awk所忽略,而不足的參數,awk將它們置爲缺省值0或空字符串,具體置爲何值,將取決於參數的使用方式。

awk函數有兩種返回方式:隱式返回和顯式返回。當awk執行到函數的結尾時,它自動地返回到調用程序,這是函數是隱式返回的。如果需要在結束之前退出函數,可以明確地使用返回語句提前退出。方法是在函數中使用形如:return 返回值 格式的語句。

例:下面的例子演示了函數的使用。在這個示例中,定義了一個名爲print_header的函數,該函數調用了兩個參數FileName和PageNum,FileName參數傳給函數當前使用的文件名,PageNum參數是當前頁的頁號。這個函數的功能是打印(顯示)出當前文件的文件名,和當前頁的頁號。完成這個功能後,這個函數將返回下一頁的頁號。

nawk
>'BEGIN{pageno=1;file=FILENAME
>pageno=print_header(file,pageno);#調用函數print_header
>printf("當前頁頁號是:%d/n",pageno);
>}

>#定義函數print_header
>function print_header(FileName,PageNum){
>printf("%s %d/n",FileName,PageNum); >PageNum++;return PageNUm;
>}
>}' myfile

執行這個程序將顯示如下內容:

myfile 1
當前頁頁號是:2

awk高級輸入輸出

1.讀取下一條記錄:

awk的next語句導致awk讀取下一個記錄並完成模式匹配,然後立即執行相應的操作。通常它用匹配的模式執行操作中的代碼。next導致這個記錄的任何額外匹配模式被忽略。

2.簡單地讀取一條記錄

awk的 getline語句用於簡單地讀取一條記錄。如果用戶有一個數據記錄類似兩個物理記錄,那麼getline將尤其有用。它完成一般字段的分離(設置字段變量$0 FNR NF NR)。如果成功則返回1,失敗則返回0(到達文件尾)。如果需簡單地讀取一個文件,則可以編寫以下代碼:

例:示例getline的使用

{while(getline==1)
{
#process the inputted fields
}
}

也可以使getline保存輸入數據在一個字段中,而不是通過使用getline variable的形式處理一般字段。當使用這種方式時,NF被置成0,FNR和NR被增值。

用戶也可以使用getline<"filename"方式從一個給定的文件中輸入數據,而不是從命令行所列內容輸入數據。此時,getline將完成一般字段分離(設置字段變量$0和NF)。如果文件不存在,返回-1,成功,返回1,返回0表示失敗。用戶可以從給定文件中讀取數據到一個變量中,也可以用stdin(標準輸入設備)或一個包含這個文件名的變量代替filename。值得注意的是當使用這種方式時不修改FNR和NR。

另一種使用getline語句的方法是從UNIX命令接受輸入,例如下面的例子:

例:示例從UNIX命令接受輸入

{while("who -u"|getline)
{
#process each line from the who command
}
}

當然,也可以使用如下形式:

"command" | getline variable

3.關閉文件:

awk中允許在程序中關閉一個輸入或輸出文件,方法是使用awk的close語句。

close("filename")

filename可以是getline打開的文件(也可以是stdin,包含文件名的變量或者getline使用的確切命令)。或一個輸出文件(可以是stdout,包含文件名的變量或使用管道的確切命令)。

4.輸出到一個文件:

awk中允許用如下方式將結果輸出到一個文件:

printf("hello word!/n")>"datafile"

printf("hello word!/n")>>"datafile"

5.輸出到一個命令

awk中允許用如下方式將結果輸出到一個命令:

printf("hello word!/n")|"sort-t','"

awk與shell script混合編程

因爲awk可以作爲一個shell命令使用,因此awk能與shell批處理程序很好的融合在一起,這給實現awk與shell程序的混合編程提供了可能。實現混合編程的關鍵是awk與shell script之間的對話,換言之,就是awk與shell script之間的信息交流:awk從shell script中獲取所需的信息(通常是變量的值)、在awk中執行shell命令行、shell script將命令執行的結果送給awk處理以及shell script讀取awk的執行結果等等。

1.awk讀取Shell script程序變量

在awk中我們可以通過“'$變量名'”的方式讀取sell scrpit程序中的變量。

例:在下面的示例中,我們將讀取sell scrpit程序中的變量Name,該變量存放的是文本myfile的撰寫者,awk將打印出這個人名。

$cat writename
:
# @(#)
#
.
.
.
Name="張三" nawk 'BEGIN {name="'Name'";/ printf("/t%s/t撰寫者%s/n",FILENAME,name");}/
{...}END{...}' myfile
.
.
.

2.將shell命令的執行結果送給awk處理

作爲信息傳送的一種方法,我們可以將一條shell命令的結果通過管道線(|)傳遞給awk處理:

例:示例awk處理shell命令的執行結果

$who -u | awk '{printf("%s正在執行%s/n",$2,$1)}'

該命令將打印出註冊終端正在執行的程序名。

3.shell script程序讀awk的執行結果

爲了實現shell script程序讀取awk執行的結果,我們可以採取一些特殊的方法,例如我們可以用變量名=`awk語句`的形式將awk執行的結果存放入一個shell script變量。當然也可以用管道線的方法將awk執行結果傳遞給shell script程序處理。

例:作爲傳送消息的機制之一,UNIX提供了一個向其所有用戶傳送消息的命令wall(意思是write to all寫給所有用戶),該命令允許向所有工作中的用戶(終端)發送消息。爲此,我們可以通過一段shell批處理程序wall.shell來模擬這一程序(事實上比較老的版本中wall就是一段shell批處理程序:

$cat wall.shell
:
# @(#) wall.shell:發送消息給每個已註冊終端
#
cat >/tmp/$$
#用戶錄入消息文本 who -u | awk '{print $2}' | while read tty
do
cat /tmp/$$>$tty
done

在這個程序裏,awk接受who -u命令的執行結果,該命令打印出所有已註冊終端的信息,其中第二個字段是已註冊終端的設備名,因此用awk命令析出該設備名,然後用while read tty語句循環讀出這些文件名到變量(shell script變量)tty中,作爲信息傳送的終結地址。

4.在awk中執行shell命令行----嵌入函數system()

system()是一個不適合字符或數字類型的嵌入函數,該函數的功能是處理作爲參數傳遞給它的字符串。system對這個參數的處理就是將其作爲命令處理,也就是說將其當作命令行一樣加以執行。這使得用戶在自己的awk程序需要時可以靈活地執行命令或腳本。

例:下面的程序將使用system嵌入函數打印用戶編制好的報表文件,這個文件存放在名爲myreport.txt的文件中。爲簡約起見,我們只列出了其END部分:

.
.
.
END {close("myreport.txt");system("lp myreport.txt");}

在這個示例中,我們首先使用close語句關閉了文件myreport.txt文件,然後使用system嵌入函數將myreport.txt送入打印機打印。

寫到這裏,我不得不跟朋友們說再見了,實在地說,這些內容仍然是awk的初步知識,電腦永遠是前進的科學,awk也不例外,本篇所能做的只是在你前行的漫漫長途中鋪平一段小小開端,剩下的路還得靠你自己去走。老實說,如果本文真能給你前行的路上帶來些許的方便,那本人就知足了!

如對本篇有任何疑問,請E-mail To:[email protected]或到主頁http://chizling.yeah.net中留言。


附錄:

1.awk的常規表達式元字符

/ 換碼序列
^ 在字符串的開頭開始匹配
$ 在字符串的結尾開始匹配
. 與任何單個字符串匹配
[ABC] 與[]內的任一字符匹配
[A-Ca-c] 與A-C及a-c範圍內的字符匹配(按字母表順序)
[^ABC] 與除[]內的所有字符以外的任一字符匹配
Desk|Chair 與Desk和Chair中的任一個匹配
[ABC][DEF] 關聯。與A、B、C中的任一字符匹配,且其後要跟D、E、F中的任一個字符。
* 與A、B或C中任一個出現0次或多次的字符相匹配
+ 與A、B或C中任何一個出現1次或多次的字符相匹配
? 與一個空串或A、B或C在任何一個字符相匹配
(Blue|Black)berry 合併常規表達式,與Blueberry或Blackberry相匹配

2.awk算術運算符

運算符 用途
------------------
x^y x的y次冪
x**y 同上
x%y 計算x/y的餘數(求模)
x+y x加y
x-y x減y
x*y x乘y
x/y x除y
-y 負y(y的開關符號);也稱一目減
++y y加1後使用y(前置加)
y++ 使用y值後加1(後綴加)
--y y減1後使用y(前置減)
y-- 使用後y減1(後綴減)
x=y 將y的值賦給x
x+=y 將x+y的值賦給x
x-=y 將x-y的值賦給x
x*=y 將x*y的值賦給x
x/=y 將x/y的值賦給x x%=y 將x%y的值賦給x
x^=y 將x^y的值賦給x
x**=y 將x**y的值賦給x

3.awk允許的測試:

操作符 含義

x==y x等於y
x!=y x不等於y
x>y x大於y
x>=y x大於或等於y
x<y x小於y
x<=y x小於或等於y?
x~re x匹配正則表達式re?
x!~re x不匹配正則表達式re?

4.awk的操作符(按優先級升序排列)

= 、+=、 -=、 *= 、/= 、 %=
||
&&
> >= < <= == != ~ !~
xy (字符串連結,'x''y'變成"xy")
+ -
* / %
++ --

5.awk內置變量(預定義變量)

說明:表中v項表示第一個支持變量的工具(下同):A=awk,N=nawk,P=POSIX awk,G=gawk

V 變量 含義 缺省值
--------------------------------------------------------
N ARGC 命令行參數個數
G ARGIND 當前被處理文件的ARGV標誌符
N ARGV 命令行參數數組
G CONVFMT 數字轉換格式 %.6g
P ENVIRON UNIX環境變量
N ERRNO UNIX系統錯誤消息
G FIELDWIDTHS 輸入字段寬度的空白分隔字符串
A FILENAME 當前輸入文件的名字
P FNR 當前記錄數
A FS 輸入字段分隔符 空格
G IGNORECASE 控制大小寫敏感0(大小寫敏感)
A NF 當前記錄中的字段個數
A NR 已經讀出的記錄數
A OFMT 數字的輸出格式 %.6g
A OFS 輸出字段分隔符 空格
A ORS 輸出的記錄分隔符 新行
A RS 輸入的記錄他隔符 新行
N RSTART 被匹配函數匹配的字符串首
N RLENGTH 被匹配函數匹配的字符串長度
N SUBSEP 下標分隔符 "/034"

6.awk的內置函數

V 函數 用途或返回值
------------------------------------------------
N gsub(reg,string,target) 每次常規表達式reg匹配時替換target中的string
N index(search,string) 返回string中search串的位置
A length(string) 求串string中的字符個數
N match(string,reg) 返回常規表達式reg匹配的string中的位置
N printf(format,variable) 格式化輸出,按format提供的格式輸出變量variable。
N split(string,store,delim) 根據分界符delim,分解string爲store的數組元素
N sprintf(format,variable) 返回一個包含基於format的格式化數據,variables是要放到串中的數據
G strftime(format,timestamp) 返回一個基於format的日期或者時間串,timestmp是systime()函數返回的時間
N sub(reg,string,target) 第一次當常規表達式reg匹配,替換target串中的字符串
A substr(string,position,len) 返回一個以position開始len個字符的子串
P totower(string) 返回string中對應的小寫字符
P toupper(string) 返回string中對應的大寫字符
A atan(x,y) x的餘切(弧度)
N cos(x) x的餘弦(弧度)
A exp(x) e的x冪
A int(x) x的整數部分
A log(x) x的自然對數值
N rand() 0-1之間的隨機數
N sin(x) x的正弦(弧度)
A sqrt(x) x的平方根
A srand(x) 初始化隨機數發生器。如果忽略x,則使用system()
G system() 返回自1970年1月1日以來經過的時間(按秒計算)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章