posts - 188,comments - 176,trackbacks - 0

           
          什么是awk?

          你可能對(duì)UNIX比較熟悉,但你可能對(duì)awk很陌生,這一點(diǎn)也不奇怪,的確,與其優(yōu)秀的功能相比,awk還遠(yuǎn)沒(méi)達(dá)到它應(yīng)有的知名度。awk是什么?與其它大多數(shù)UNIX命令不同的是,從名字上看,我們不可能知道awk的功能:它既不是具有獨(dú)立意義的英文單詞,也不是幾個(gè)相關(guān)單詞的縮寫(xiě)。事實(shí)上,awk是三個(gè)人名的縮寫(xiě),他們是:Aho、(Peter)Weinberg和(Brain)Kernighan。正是這三個(gè)人創(chuàng)造了awk---一個(gè)優(yōu)秀的樣式掃描與處理工具。

          AWK的功能是什么?與sed和grep很相似,awk是一種樣式掃描與處理工具。但其功能卻大大強(qiáng)于sed和grep。awk提供了極其強(qiáng)大的功能:它幾乎可以完成grep和sed所能完成的全部工作,同時(shí),它還可以可以進(jìn)行樣式裝入、流控制、數(shù)學(xué)運(yùn)算符、進(jìn)程控制語(yǔ)句甚至于內(nèi)置的變量和函數(shù)。它具備了一個(gè)完整的語(yǔ)言所應(yīng)具有的幾乎所有精美特性。實(shí)際上,awk的確擁有自己的語(yǔ)言:awk程序設(shè)計(jì)語(yǔ)言,awk的三位創(chuàng)建者已將它正式定義為:樣式掃描和處理語(yǔ)言。

          為什么使用awk?

          即使如此,你也許仍然會(huì)問(wèn),我為什么要使用awk?

          使用awk的第一個(gè)理由是基于文本的樣式掃描和處理是我們經(jīng)常做的工作,awk所做的工作有些象數(shù)據(jù)庫(kù),但與數(shù)據(jù)庫(kù)不同的是,它處理的是文本文件,這些文件沒(méi)有專(zhuān)門(mén)的存儲(chǔ)格式,普通的人們就能編輯、閱讀、理解和處理它們。而數(shù)據(jù)庫(kù)文件往往具有特殊的存儲(chǔ)格式,這使得它們必須用數(shù)據(jù)庫(kù)處理程序來(lái)處理它們。既然這種類(lèi)似于數(shù)據(jù)庫(kù)的處理工作我們經(jīng)常會(huì)遇到,我們就應(yīng)當(dāng)找到處理它們的簡(jiǎn)便易行的方法,UNIX有很多這方面的工具,例如sed 、grep、sort以及find等等,awk是其中十分優(yōu)秀的一種。

          使用awk的第二個(gè)理由是awk是一個(gè)簡(jiǎn)單的工具,當(dāng)然這是相對(duì)于其強(qiáng)大的功能來(lái)說(shuō)的。的確,UNIX有許多優(yōu)秀的工具,例如UNIX天然的開(kāi)發(fā)工具C語(yǔ)言及其延續(xù)C++就非常的優(yōu)秀。但相對(duì)于它們來(lái)說(shuō),awk完成同樣的功能要方便和簡(jiǎn)捷得多。這首先是因?yàn)閍wk提供了適應(yīng)多種需要的解決方案:從解決簡(jiǎn)單問(wèn)題的awk命令行到復(fù)雜而精巧的awk程序設(shè)計(jì)語(yǔ)言,這樣做的好處是,你可以不必用復(fù)雜的方法去解決本來(lái)很簡(jiǎn)單的問(wèn)題。例如,你可以用一個(gè)命令行解決簡(jiǎn)單的問(wèn)題,而C不行,即使一個(gè)再簡(jiǎn)單的程序,C語(yǔ)言也必須經(jīng)過(guò)編寫(xiě)、編譯的全過(guò)程。其次,awk本身是解釋執(zhí)行的,這就使得awk程序不必經(jīng)過(guò)編譯的過(guò)程,同時(shí),這也使得它與shell script程序能夠很好的契合。最后,awk本身較C語(yǔ)言簡(jiǎn)單,雖然awk吸收了C語(yǔ)言很多優(yōu)秀的成分,熟悉C語(yǔ)言會(huì)對(duì)學(xué)習(xí)awk有很大的幫助,但awk本身不須要會(huì)使用C語(yǔ)言——一種功能強(qiáng)大但需要大量時(shí)間學(xué)習(xí)才能掌握其技巧的開(kāi)發(fā)工具。

          使用awk的第三個(gè)理由是awk是一個(gè)容易獲得的工具。與C和C++語(yǔ)言不同,awk只有一個(gè)文件(/bin/awk),而且?guī)缀趺總€(gè)版本的UNIX都提供各自版本的awk,你完全不必費(fèi)心去想如何獲得awk。但C語(yǔ)言卻不是這樣,雖然C語(yǔ)言是UNIX天然的開(kāi)發(fā)工具,但這個(gè)開(kāi)發(fā)工具卻是單獨(dú)發(fā)行的,換言之,你必須為你的UNIX版本的C語(yǔ)言開(kāi)發(fā)工具單獨(dú)付費(fèi)(當(dāng)然使用D版者除外),獲得并安裝它,然后你才可以使用它。

          基于以上理由,再加上awk強(qiáng)大的功能,我們有理由說(shuō),如果你要處理與文本樣式掃描相關(guān)的工作,awk應(yīng)該是你的第一選擇。在這里有一個(gè)可遵循的一般原則:如果你用普通的shell工具或shell script有困難的話(huà),試試awk,如果awk仍不能解決問(wèn)題,則便用C語(yǔ)言,如果C語(yǔ)言仍然失敗,則移至C++。

          awk的調(diào)用方式

          前面曾經(jīng)說(shuō)過(guò),awk提供了適應(yīng)多種需要的不同解決方案,它們是:

          一、awk命令行,你可以象使用普通UNIX命令一樣使用awk,在命令行中你也可以使用awk程序設(shè)計(jì)語(yǔ)言,雖然awk支持多行的錄入,但是錄入長(zhǎng)長(zhǎng)的命令行并保證其正確無(wú)誤卻是一件令人頭疼的事,因此,這種方法一般只用于解決簡(jiǎn)單的問(wèn)題。當(dāng)然,你也可以在shell script程序中引用awk命令行甚至awk程序腳本。

          二、使用-f選項(xiàng)調(diào)用awk程序。awk允許將一段awk程序?qū)懭胍粋€(gè)文本文件,然后在awk命令行中用-f選項(xiàng)調(diào)用并執(zhí)行這段程序。具體的方法我們將在后面的awk語(yǔ)法中講到。

          三、利用命令解釋器調(diào)用awk程序:利用UNIX支持的命令解釋器功能,我們可以將一段awk程序?qū)懭胛谋疚募?,然后在它的第一行加?
          #!/bin/awk -f
          并賦予這個(gè)文本文件以執(zhí)行的權(quán)限。這樣做之后,你就可以在命令行中用類(lèi)似于下面這樣的方式調(diào)用并執(zhí)行這段awk程序了。

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

          awk的語(yǔ)法:

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

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

          參數(shù)說(shuō)明:

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

          parameter: 該參數(shù)幫助為不同的變量賦值。

          'prog': awk的程序語(yǔ)句段。這個(gè)語(yǔ)句段必須用單拓號(hào):'和'括起,以防被shell解釋。這個(gè)程序語(yǔ)句段的標(biāo)準(zhǔn)形式為:

          'pattern {action}'

          其中pattern參數(shù)可以是egrep正則表達(dá)式中的任何一個(gè),它可以使用語(yǔ)法/re/再加上一些樣式匹配技巧構(gòu)成。與sed類(lèi)似,你也可以使用","分開(kāi)兩樣式以選擇某個(gè)范圍。關(guān)于匹配的細(xì)節(jié),你可以參考附錄,如果仍不懂的話(huà),找本UNIX書(shū)學(xué)學(xué)grep和sed(本人是在學(xué)習(xí)ed時(shí)掌握匹配技術(shù)的)。action參數(shù)總是被大括號(hào)包圍,它由一系統(tǒng)awk語(yǔ)句組成,各語(yǔ)句之間用";"分隔。awk解釋它們,并在pattern給定的樣式匹配的記錄上執(zhí)行其操作。與shell類(lèi)似,你也可以使用“#”作為注釋符,它使“#”到行尾的內(nèi)容成為注釋?zhuān)诮忉寛?zhí)行時(shí),它們將被忽略。你可以省略pattern和action之一,但不能兩者同時(shí)省略,當(dāng)省略pattern時(shí)沒(méi)有樣式匹配,表示對(duì)所有行(記錄)均執(zhí)行操作,省略action時(shí)執(zhí)行缺省的操作——在標(biāo)準(zhǔn)輸出上顯示。

          -f progfile:允許awk調(diào)用并執(zhí)行progfile指定有程序文件。progfile是一個(gè)文本文件,他必須符合awk的語(yǔ)法。

          in_file:awk的輸入文件,awk允許對(duì)多個(gè)輸入文件進(jìn)行處理。值得注意的是awk不修改輸入文件。如果未指定輸入文件,awk將接受標(biāo)準(zhǔn)輸入,并將結(jié)果顯示在標(biāo)準(zhǔn)輸出上。awk支持輸入輸出重定向。

          awk的記錄、字段與內(nèi)置變量:

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

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

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

          awk的內(nèi)置函數(shù)

          awk之所以成為一種優(yōu)秀的程序設(shè)計(jì)語(yǔ)言的原因之一是它吸收了某些優(yōu)秀的程序設(shè)計(jì)語(yǔ)言(例如C)語(yǔ)言的許多優(yōu)點(diǎn)。這些優(yōu)點(diǎn)之一就是內(nèi)置函數(shù)的使用,awk定義并支持了一系列的內(nèi)置函數(shù),由于這些函數(shù)的使用,使得awk提供的功能更為完善和強(qiáng)大,例如,awk使用了一系列的字符串處理內(nèi)置函數(shù)(這些函數(shù)看起來(lái)與C語(yǔ)言的字符串處理函數(shù)相似,其使用方式與C語(yǔ)言中的函數(shù)也相差無(wú)幾),正是由于這些內(nèi)置函數(shù)的使用,使awk處理字符串的功能更加強(qiáng)大。本文后面的附錄中列有一般的awk所提供的內(nèi)置函數(shù),這些內(nèi)置函數(shù)也許與你的awk版本有些出入,因此,在使用之前,最好參考一下你的系統(tǒng)中的聯(lián)機(jī)幫助。

          作為內(nèi)置函數(shù)的一個(gè)例子,我們將在這里介紹awk的printf函數(shù),這個(gè)函數(shù)使得awk與c語(yǔ)言的輸出相一致。實(shí)際上,awk中有許多引用形式都是從C語(yǔ)言借用過(guò)來(lái)的。如果你熟悉C語(yǔ)言,你也許會(huì)記得其中的printf函數(shù),它提供的強(qiáng)大格式輸出功能曾經(jīng)帶我們?cè)S多的方便。幸運(yùn)的是,我們?cè)赼wk中又和它重逢了。awk中printf幾乎與C語(yǔ)言中一模一樣,如果你熟悉C語(yǔ)言的話(huà),你完全可以照C語(yǔ)言的模式使用awk中的printf。因此在這里,我們只給出一個(gè)例子,如果你不熟悉的話(huà),請(qǐng)隨便找一本C語(yǔ)言的入門(mén)書(shū)翻翻。

          例:顯示文件myfile中的行號(hào)和第3字段:

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

          在命令行使用awk

          按照順序,我們應(yīng)當(dāng)講解awk程序設(shè)計(jì)的內(nèi)容了,但在講解之前,我們將用一些例子來(lái)對(duì)前面的知識(shí)進(jìn)行回顧,這些例子都是在命令行中使用的,由此我們可以知道在命令行中使用awk是多么的方便。這樣做的原因一方面是為下面的內(nèi)容作鋪墊,另一方面是介紹一些解決簡(jiǎn)單問(wèn)題的方法,我們完全沒(méi)有必要用復(fù)雜的方法來(lái)解決簡(jiǎn)單的問(wèn)題----既然awk提供了較為簡(jiǎn)單的方法的話(huà)。

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

          $awk '/sun/{print}' mydoc

          由于顯示整個(gè)記錄(全行)是awk的缺省動(dòng)作,因此可以省略action項(xiàng)。

          $awk '/sun/' mydoc

          例:下面是一個(gè)較為復(fù)雜的匹配的示例:

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

          它將顯示第一個(gè)匹配Sun或sun的行與第一個(gè)匹配Moon或moon的行之間的行,并顯示到標(biāo)準(zhǔn)輸出上。

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

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

          該命令行將顯示文本myfile中所有超過(guò)80個(gè)字符的行號(hào),在這里,用$0表示整個(gè)記錄(行),同時(shí),內(nèi)置變量NR不使用標(biāo)志符'$'。

          例:作為一個(gè)較為實(shí)際的例子,我們假設(shè)要對(duì)UNIX中的用戶(hù)進(jìn)行安全性檢查,方法是考察/etc下的passwd文件,檢查其中的passwd字段(第二字段)是否為"*",如不為"*",則表示該用戶(hù)沒(méi)有設(shè)置密碼,顯示出這些用戶(hù)名(第一字段)。我們可以用如下語(yǔ)句實(shí)現(xiàn):

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

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

          (自測(cè)試):利用awk打印循環(huán)文本aaa.txt中每行的每一列的值
          aaa.txt文本的內(nèi)容如下:
          1|aa|11.1|12
          2|bb|22.2|13
          3|cc|33.3|14

          cat aaa.txt |while read tmp
          do
          sh_taskindex=$(echo $tmp | awk -F "|" '{ print $1 }')
          sh_commonindex=$(echo $tmp | awk -F "|" '{ print $2 }')
          sh_tasktype=$(echo $tmp | awk -F "|" '{ print $3 }')
          sh_date=$(echo $tmp | awk -F "|" '{ print $4 }')
          echo  $sh_taskindex $sh_commonindex $sh_tasktype $sh_date
          done

          cat aaa.txt |while read tmp
          do
          sh_taskindex=`echo $tmp | awk -F "|" '{ print $1 }'`
          sh_commonindex=`echo $tmp | awk -F "|" '{ print $2 }'`
          sh_tasktype=`echo $tmp | awk -F "|" '{ print $3 }'`
          sh_date=`echo $tmp | awk -F "|" '{ print $4 }'`
          echo  $sh_taskindex $sh_commonindex $sh_tasktype $sh_date
          done


          awk的變量

          如同其它程序設(shè)計(jì)語(yǔ)言一樣,awk允許在程序語(yǔ)言中設(shè)置變量,事實(shí)上,提供變量的功能是程序設(shè)計(jì)語(yǔ)言的其本要求,不提供變量的程序設(shè)計(jì)語(yǔ)言本人還從未見(jiàn)過(guò)。

          awk提供兩種變量,一種是awk內(nèi)置的變量,這前面我們已經(jīng)講過(guò),需要著重指出的是,與后面提到的其它變量不同的是,在awk程序中引用內(nèi)置變量不需要使用標(biāo)志符"$"(回憶一下前面講過(guò)的NR的使用)。awk提供的另一種變量是自定義變量。awk允許用戶(hù)在awk程序語(yǔ)句中定義并調(diào)用自已的變量。當(dāng)然這種變量不能與內(nèi)置變量及其它awk保留字相同,在awk中引用自定義變量必須在它前面加上標(biāo)志符"$"。與C語(yǔ)言不同的是,awk中不需要對(duì)變量進(jìn)行初始化,awk根據(jù)其在awk中第一次出現(xiàn)的形式和上下文確定其具體的數(shù)據(jù)類(lèi)型。當(dāng)變量類(lèi)型不確定時(shí),awk默認(rèn)其為字符串類(lèi)型。這里有一個(gè)技巧:如果你要讓你的awk程序知道你所使用的變量的明確類(lèi)型,你應(yīng)當(dāng)在在程序中給它賦初值。在后面的實(shí)例中,我們將用到這一技巧。

          運(yùn)算與判斷:

          作為一種程序設(shè)計(jì)語(yǔ)言所應(yīng)具有的特點(diǎn)之一,awk支持多種運(yùn)算,這些運(yùn)算與C語(yǔ)言提供的幾本相同:如+、-、*、/、%等等,同時(shí),awk也支持C語(yǔ)言中類(lèi)似++、--、+=、-=、=+、=-之類(lèi)的功能,這給熟悉C語(yǔ)言的使用者編寫(xiě)awk程序帶來(lái)了極大的方便。作為對(duì)運(yùn)算功能的一種擴(kuò)展,awk還提供了一系列內(nèi)置的運(yùn)算函數(shù)(如log、sqr、cos、sin等等)和一些用于對(duì)字符串進(jìn)行操作(運(yùn)算)的函數(shù)(如length、substr等等)。這些函數(shù)的引用大大的提高了awk的運(yùn)算功能。

          作為對(duì)條件轉(zhuǎn)移指令的一部分,關(guān)系判斷是每種程序設(shè)計(jì)語(yǔ)言都具備的功能,awk也不例外。awk中允許進(jìn)行多種測(cè)試,如常用的==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、>=(小于等于)等等,同時(shí),作為樣式匹配,還提供了~(匹配于)和!~(不匹配于)判斷。

          作為對(duì)測(cè)試的一種擴(kuò)充,awk也支持用邏輯運(yùn)算符:!(非)、&&(與)、||(或)和括號(hào)()進(jìn)行多重判斷,這大大增強(qiáng)了awk的功能。本文的附錄中列出了awk所允許的運(yùn)算、判斷以及操作符的優(yōu)先級(jí)。

          awk的流程控制

          流程控制語(yǔ)句是任何程序設(shè)計(jì)語(yǔ)言都不能缺少的部分。任何好的語(yǔ)言都有一些執(zhí)行流程控制的語(yǔ)句。awk提供的完備的流程控制語(yǔ)句類(lèi)似于C語(yǔ)言,這給我們編程帶來(lái)了極大的方便。

          1、BEGIN和END:

          在awk中兩個(gè)特別的表達(dá)式,BEGIN和END,這兩者都可用于pattern中(參考前面的awk語(yǔ)法),提供BEGIN和END的作用是給程序賦予初始狀態(tài)和在程序結(jié)束之后執(zhí)行一些掃尾的工作。任何在BEGIN之后列出的操作(在{}內(nèi))將在awk開(kāi)始掃描輸入之前執(zhí)行,而END之后列出的操作將在掃描完全部的輸入之后執(zhí)行。因此,通常使用BEGIN來(lái)顯示變量和預(yù)置(初始化)變量,使用END來(lái)輸出最終結(jié)果。

          例:累計(jì)銷(xiāo)售文件xs中的銷(xiāo)售金額(假設(shè)銷(xiāo)售金額在記錄的第三字段):

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

          在這里,BEGIN預(yù)置了內(nèi)部變量FS(字段分隔符)和自定義變量total,同時(shí)在掃描之前顯示出輸出行頭。而END則在掃描完成后打印出總合計(jì)。

          2、流程控制語(yǔ)句
          awk提供了完備的流程控制語(yǔ)句,其用法與C語(yǔ)言類(lèi)似。下面我們一一加以說(shuō)明:

          2.1、if...else語(yǔ)句:

          格式:
          if(表達(dá)式)
          語(yǔ)句1
          else
          語(yǔ)句2

          格式中"語(yǔ)句1"可以是多個(gè)語(yǔ)句,如果你為了方便awk判斷也方便你自已閱讀,你最好將多個(gè)語(yǔ)句用{}括起來(lái)。awk分枝結(jié)構(gòu)允許嵌套,其格式為:

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

          當(dāng)然實(shí)際操作過(guò)程中你可能不會(huì)用到如此復(fù)雜的分枝結(jié)構(gòu),這里只是為了給出其樣式罷了。

          2.2、while語(yǔ)句

          格式為:

          while(表達(dá)式)
          語(yǔ)句

          2.3、do-while語(yǔ)句

          格式為:

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

          2.4、for語(yǔ)句

          格式為:

          for(初始表達(dá)式;終止條件;步長(zhǎng)表達(dá)式)
          {語(yǔ)句}

          在awk的 while、do-while和for語(yǔ)句中允許使用break,continue語(yǔ)句來(lái)控制流程走向,也允許使用exit這樣的語(yǔ)句來(lái)退出。break中斷當(dāng)前正在執(zhí)行的循環(huán)并跳到循環(huán)外執(zhí)行下一條語(yǔ)句。continue從當(dāng)前位置跳到循環(huán)開(kāi)始處執(zhí)行。對(duì)于exit的執(zhí)行有兩種情況:當(dāng)exit語(yǔ)句不在END中時(shí),任何操作中的exit命令表現(xiàn)得如同到了文件尾,所有模式或操作執(zhí)行將停止,END模式中的操作被執(zhí)行。而出現(xiàn)在END中的exit將導(dǎo)致程序終止。


          awk中的自定義函數(shù)

          定義和調(diào)用用戶(hù)自己的函數(shù)是幾乎每個(gè)高級(jí)語(yǔ)言都具有的功能,awk也不例外,但原始的awk并不提供函數(shù)功能,只有在nawk或較新的awk版本中才可以增加函數(shù)。

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

          awk函數(shù)的定義方法如下:

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

          在gawk中允許將function省略為func,但其它版本的awk不允許。函數(shù)名必須是一個(gè)合法的標(biāo)志符,參數(shù)表中可以不提供參數(shù)(但在調(diào)用函數(shù)時(shí)函數(shù)名后的一對(duì)括號(hào)仍然是不可缺少的),也可以提供一個(gè)或多個(gè)參數(shù)。與C語(yǔ)言相似,awk的參數(shù)也是通過(guò)值來(lái)傳遞的。

          在awk中調(diào)用函數(shù)比較簡(jiǎn)單,其方法與C語(yǔ)言相似,但awk比C語(yǔ)言更為靈活,它不執(zhí)行參數(shù)有效性檢查。換句話(huà)說(shuō),在你調(diào)用函數(shù)時(shí),可以列出比函數(shù)預(yù)計(jì)(函數(shù)定義中規(guī)定)的多或少的參數(shù),多余的參數(shù)會(huì)被awk所忽略,而不足的參數(shù),awk將它們置為缺省值0或空字符串,具體置為何值,將取決于參數(shù)的使用方式。

          awk函數(shù)有兩種返回方式:隱式返回和顯式返回。當(dāng)awk執(zhí)行到函數(shù)的結(jié)尾時(shí),它自動(dòng)地返回到調(diào)用程序,這是函數(shù)是隱式返回的。如果需要在結(jié)束之前退出函數(shù),可以明確地使用返回語(yǔ)句提前退出。方法是在函數(shù)中使用形如:return 返回值 格式的語(yǔ)句。

          例:下面的例子演示了函數(shù)的使用。在這個(gè)示例中,定義了一個(gè)名為print_header的函數(shù),該函數(shù)調(diào)用了兩個(gè)參數(shù)FileName和PageNum,F(xiàn)ileName參數(shù)傳給函數(shù)當(dāng)前使用的文件名,PageNum參數(shù)是當(dāng)前頁(yè)的頁(yè)號(hào)。這個(gè)函數(shù)的功能是打?。@示)出當(dāng)前文件的文件名,和當(dāng)前頁(yè)的頁(yè)號(hào)。完成這個(gè)功能后,這個(gè)函數(shù)將返回下一頁(yè)的頁(yè)號(hào)。

          nawk
          >'BEGIN{pageno=1;file=FILENAME
          >pageno=print_header(file,pageno);#調(diào)用函數(shù)print_header
          >printf("當(dāng)前頁(yè)頁(yè)號(hào)是:%d\n",pageno);
          >}

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

          執(zhí)行這個(gè)程序?qū)@示如下內(nèi)容:

          myfile 1
          當(dāng)前頁(yè)頁(yè)號(hào)是:2


          (自測(cè)試):在這個(gè)示例中,文本aaa.txt和bbb.txt分別對(duì)應(yīng)Oracle庫(kù)中的兩張表aaa和bbb的信息(導(dǎo)出的結(jié)果,其中aaa和bbb通過(guò)taskindex字段關(guān)聯(lián)),現(xiàn)欲將數(shù)據(jù)庫(kù)映射到文本中的信息,通過(guò)taskindex來(lái)對(duì)應(yīng)打印聯(lián)合的結(jié)果到屏幕上。

          aaa.txt和bbb.txt的文本內(nèi)容如下:
          zxin10@linux:~/smssend/awk> cat aaa.txt bbb.txt

          1|aa|11.1|12
          2|bb|22.2|13
          3|cc|33.3|14

          12|20080301
          13|20080802
          14|20090303

          awk.sh的原型如下:
          #!/bin/sh
          group_file()
          {
              echo $1,$2 #打印調(diào)用函數(shù)的實(shí)參值
              awk -F "|" '     
                  NR==FNR {
                          sub("^[\011\040]+", "", $0);
                          sub("[\011\040]+$", "", $0);
                          userlist[$4]=$0
                          printf("%s\n",$4);#$1為第一個(gè)文本每行第四列
                          #userlist_col1[$1]=$1
                          #userlist_col2[$1]=$2
                          #userlist_col3[$1]=$3
                          #userlist_col4[$1]=$4
                             
                  }   
                  NR>FNR {    
                      if($1 in userlist){
                           printf("%s\n",$1);#$1為第二個(gè)文本每行第一列
                           printf("%s|%s\n",userlist[$1],$2);
                          #printf("%s|%s|%s|%s\n",userlist_col2[$1],userlist_col3[$1],userlist_col4[$1],$2);
                      }          
                  }
                  ' $1 $2
          }
          group_file aaa.txt bbb.txt

          執(zhí)行shell結(jié)果:
          aaa.txt,bbb.txt
          1|aa|11.1|12|20080301
          2|bb|22.2|13|20080802
          3|cc|33.3|14|20090303


          awk高級(jí)輸入輸出

          1.讀取下一條記錄:

          awk的next語(yǔ)句導(dǎo)致awk讀取下一個(gè)記錄并完成模式匹配,然后立即執(zhí)行相應(yīng)的操作。通常它用匹配的模式執(zhí)行操作中的代碼。next導(dǎo)致這個(gè)記錄的任何額外匹配模式被忽略。

          2.簡(jiǎn)單地讀取一條記錄

          awk的 getline語(yǔ)句用于簡(jiǎn)單地讀取一條記錄。如果用戶(hù)有一個(gè)數(shù)據(jù)記錄類(lèi)似兩個(gè)物理記錄,那么getline將尤其有用。它完成一般字段的分離(設(shè)置字段變量$0 FNR NF NR)。如果成功則返回1,失敗則返回0(到達(dá)文件尾)。如果需簡(jiǎn)單地讀取一個(gè)文件,則可以編寫(xiě)以下代碼:

          例:示例getline的使用

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

          也可以使getline保存輸入數(shù)據(jù)在一個(gè)字段中,而不是通過(guò)使用getline variable的形式處理一般字段。當(dāng)使用這種方式時(shí),NF被置成0,F(xiàn)NR和NR被增值。

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

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

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

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

          當(dāng)然,也可以使用如下形式:

          "command" | getline variable

          3.關(guān)閉文件:

          awk中允許在程序中關(guān)閉一個(gè)輸入或輸出文件,方法是使用awk的close語(yǔ)句。

          close("filename")

          filename可以是getline打開(kāi)的文件(也可以是stdin,包含文件名的變量或者getline使用的確切命令)?;蛞粋€(gè)輸出文件(可以是stdout,包含文件名的變量或使用管道的確切命令)。

          4.輸出到一個(gè)文件:

          awk中允許用如下方式將結(jié)果輸出到一個(gè)文件:

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

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

          5.輸出到一個(gè)命令

          awk中允許用如下方式將結(jié)果輸出到一個(gè)命令:

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

          awk與shell script混合編程

          因?yàn)閍wk可以作為一個(gè)shell命令使用,因此awk能與shell批處理程序很好的融合在一起,這給實(shí)現(xiàn)awk與shell程序的混合編程提供了可能。實(shí)現(xiàn)混合編程的關(guān)鍵是awk與shell script之間的對(duì)話(huà),換言之,就是awk與shell script之間的信息交流:awk從shell script中獲取所需的信息(通常是變量的值)、在awk中執(zhí)行shell命令行、shell script將命令執(zhí)行的結(jié)果送給awk處理以及shell script讀取awk的執(zhí)行結(jié)果等等。

          1.awk讀取Shell script程序變量

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

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

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

          2.將shell命令的執(zhí)行結(jié)果送給awk處理

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

          例:示例awk處理shell命令的執(zhí)行結(jié)果

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

          該命令將打印出注冊(cè)終端正在執(zhí)行的程序名。

          3.shell script程序讀awk的執(zhí)行結(jié)果

          為了實(shí)現(xiàn)shell script程序讀取awk執(zhí)行的結(jié)果,我們可以采取一些特殊的方法,例如我們可以用變量名=`awk語(yǔ)句`的形式將awk執(zhí)行的結(jié)果存放入一個(gè)shell script變量。當(dāng)然也可以用管道線(xiàn)的方法將awk執(zhí)行結(jié)果傳遞給shell script程序處理。

          例:作為傳送消息的機(jī)制之一,UNIX提供了一個(gè)向其所有用戶(hù)傳送消息的命令wall(意思是write to all寫(xiě)給所有用戶(hù)),該命令允許向所有工作中的用戶(hù)(終端)發(fā)送消息。為此,我們可以通過(guò)一段shell批處理程序wall.shell來(lái)模擬這一程序(事實(shí)上比較老的版本中wall就是一段shell批處理程序:

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

          在這個(gè)程序里,awk接受who -u命令的執(zhí)行結(jié)果,該命令打印出所有已注冊(cè)終端的信息,其中第二個(gè)字段是已注冊(cè)終端的設(shè)備名,因此用awk命令析出該設(shè)備名,然后用while read tty語(yǔ)句循環(huán)讀出這些文件名到變量(shell script變量)tty中,作為信息傳送的終結(jié)地址。

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

          system()是一個(gè)不適合字符或數(shù)字類(lèi)型的嵌入函數(shù),該函數(shù)的功能是處理作為參數(shù)傳遞給它的字符串。system對(duì)這個(gè)參數(shù)的處理就是將其作為命令處理,也就是說(shuō)將其當(dāng)作命令行一樣加以執(zhí)行。這使得用戶(hù)在自己的awk程序需要時(shí)可以靈活地執(zhí)行命令或腳本。

          例:下面的程序?qū)⑹褂胹ystem嵌入函數(shù)打印用戶(hù)編制好的報(bào)表文件,這個(gè)文件存放在名為myreport.txt的文件中。為簡(jiǎn)約起見(jiàn),我們只列出了其END部分:

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

          在這個(gè)示例中,我們首先使用close語(yǔ)句關(guān)閉了文件myreport.txt文件,然后使用system嵌入函數(shù)將myreport.txt送入打印機(jī)打印。

          寫(xiě)到這里,我不得不跟朋友們說(shuō)再見(jiàn)了,實(shí)在地說(shuō),這些內(nèi)容仍然是awk的初步知識(shí),電腦永遠(yuǎn)是前進(jìn)的科學(xué),awk也不例外,本篇所能做的只是在你前行的漫漫長(zhǎng)途中鋪平一段小小開(kāi)端,剩下的路還得靠你自己去走。老實(shí)說(shuō),如果本文真能給你前行的路上帶來(lái)些許的方便,那本人就知足了!

          如對(duì)本篇有任何疑問(wèn),請(qǐng)E-mail To:Chizlong@yeah.net或到主頁(yè)http://chizling.yeah.net中留言。


          附錄:

          1.awk的常規(guī)表達(dá)式元字符

          \ 換碼序列
          ^ 在字符串的開(kāi)頭開(kāi)始匹配
          $ 在字符串的結(jié)尾開(kāi)始匹配
          . 與任何單個(gè)字符串匹配
          [ABC] 與[]內(nèi)的任一字符匹配
          [A-Ca-c] 與A-C及a-c范圍內(nèi)的字符匹配(按字母表順序)
          [^ABC] 與除[]內(nèi)的所有字符以外的任一字符匹配
          Desk|Chair 與Desk和Chair中的任一個(gè)匹配
          [ABC][DEF] 關(guān)聯(lián)。與A、B、C中的任一字符匹配,且其后要跟D、E、F中的任一個(gè)字符。
          * 與A、B或C中任一個(gè)出現(xiàn)0次或多次的字符相匹配
          + 與A、B或C中任何一個(gè)出現(xiàn)1次或多次的字符相匹配
          ? 與一個(gè)空串或A、B或C在任何一個(gè)字符相匹配
          (Blue|Black)berry 合并常規(guī)表達(dá)式,與Blueberry或Blackberry相匹配

          2.awk算術(shù)運(yùn)算符

          運(yùn)算符 用途
          ------------------
          x^y x的y次冪
          x**y 同上
          x%y 計(jì)算x/y的余數(shù)(求模)
          x+y x加y
          x-y x減y
          x*y x乘y
          x/y x除y
          -y 負(fù)y(y的開(kāi)關(guān)符號(hào));也稱(chēng)一目減
          ++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允許的測(cè)試:

          操作符 含義

          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匹配正則表達(dá)式re?
          x!~re x不匹配正則表達(dá)式re?

          4.awk的操作符(按優(yōu)先級(jí)升序排列)

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

          5.awk內(nèi)置變量(預(yù)定義變量)

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

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

          6.awk的內(nèi)置函數(shù)

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



          轉(zhuǎn)自:http://blog.donews.com/Dophins2005/archive/2005/06/02/409594.aspx?Pending=true

          posted on 2008-03-07 12:13 cheng 閱讀(470) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Unix/Linux
          主站蜘蛛池模板: 扎囊县| 迁西县| 板桥市| 河间市| 天峨县| 昭觉县| 银川市| 隆德县| 阜康市| 碌曲县| 大埔区| 花莲县| 西昌市| 安化县| 林口县| 福海县| 县级市| 衢州市| 台东县| 邹城市| 桓仁| 界首市| 涪陵区| 永德县| 亳州市| 罗田县| 长武县| 比如县| 汽车| 宁河县| 徐州市| 太和县| 曲阜市| 资溪县| 新野县| 新余市| 灌云县| 镇坪县| 桂阳县| 江都市| 荔波县|