亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
123下一頁
最近訪問板塊 發(fā)新帖
查看: 8383 | 回復(fù): 24
打印 上一主題 下一主題

轉(zhuǎn)貼 awk Preface [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2005-09-04 09:22 |只看該作者 |倒序瀏覽
根據(jù)這壇里的鏈接找到的臺灣出品的幾篇好貼,感覺很不錯,又受到了一次基礎(chǔ)培訓(xùn)和鞏固復(fù)習(xí).所以收藏后也共享給大家,謝謝!

前言
Preface

有關(guān)本手冊 :
        這是一本AWK學(xué)習(xí)指引, 其重點(diǎn)著重于 :   
           AWK 適于解決哪些問題 ?
           AWK 常見的解題模式為何  ?
為使讀者快速掌握AWK解題的模式及特性, 本手冊系由一些較具
代表性的范例及其題解所構(gòu)成; 各范例由淺入深, 彼此間相互連貫,
范例中并對所使用的AWK語法及指令輔以必要的說明. 有關(guān)AWK的
指令, 函數(shù),...等條列式的說明則收錄于附錄中, 以利讀者往后撰寫
程序時查閱. 如此編排, 可讓讀者在短時間內(nèi)順暢地學(xué)會使用AWK
來解決問題. 建議讀者循著范例上機(jī)實習(xí), 以加深學(xué)習(xí)效果.


讀者宜先具備下列背景 :
[a.] UNIX 環(huán)境下的簡單操作及基本概念.
      例如 : 檔案編輯, 檔案復(fù)制 及 pipe, I/O Redirection 等概念

[b.] C 語言的基本語法及流程控制指令.
(AWK 指令并不多, 且其中之大部分與 C語言中之用法一致, 本手冊
中對該類指令之語法及特性不再加以繁冗的說明, 讀者若欲深究,
可自行翻閱相關(guān)的 C 語言書籍)

Overview of AWK
Why AWK
AWK 是一種程序語言. 它具有一般程序語言常見的功能.
因AWK語言具有某些特點(diǎn), 如 : 使用直譯器(Interpreter)不需先行
編譯; 變量無型別之分(Typeless), 可使用文字當(dāng)數(shù)組的注標(biāo)
(Associative Array)...等特色. 因此, 使用AWK撰寫程序比起
使用其它語言更簡潔便利且節(jié)省時間. AWK還具有一些內(nèi)建
功能, 使得AWK擅于處理具數(shù)據(jù)列(Record), 字段(Field)型
態(tài)的資料; 此外, AWK內(nèi)建有pipe的功能, 可將處理中的數(shù)據(jù)
傳送給外部的 Shell命令加以處理, 再將Shell命令處理后的
數(shù)據(jù)傳回AWK程序, 這個特點(diǎn)也使得AWK程序很容易使用
系統(tǒng)資源.
   
由于AWK具有上述特色, 在問題處理的過程, 可輕易使用
AWK來撰寫一些小工具; 這些小工具并非用來解決整個大問題,
它們只個別扮演解決問題過程的某些角色, 可藉由Shell所提供的
pipe將數(shù)據(jù)按需要傳送給不同的小工具進(jìn)行處理, 以解決整個
大問題. 這種解題方式, 使得這些小工具可因不同需求而被重復(fù)
組合及使用(reuse); 也可藉此方式來先行測試大程序原型的可行性
與正確性, 將來若需要較高的執(zhí)行速度時再用C語言來改寫.
這是AWK最常被應(yīng)用之處. 若能常常如此處理問題, 讀者可以
以更高的角度來思考抽象的問題, 而不會被拘泥于細(xì)節(jié)的部份.
本手冊為AWK入門的學(xué)習(xí)指引, 其內(nèi)容將先強(qiáng)調(diào)如何撰寫AWK程序,
未列入進(jìn)一步解題方式的應(yīng)用實例, 這部分將留待UNIX進(jìn)階手冊中
再行討論.
如何取得 AWK

一般的UNIX操作系統(tǒng), 本身即附有AWK. 不同的UNIX操作系統(tǒng)
所附的AWK其版本亦不盡相同. 若讀者所使用的系統(tǒng)上未附有AWK,
可透過 anonymous ftp 到下列地方取得 :
         phi.sinica.edu.tw:/pub/gnu
         ftp.edu.tw:/UNIX/gnu
         prep.ai.mit.edu:/pub/gnu

--------------------------------------------------------------------------------

How AWK works

為便于解釋AWK程序架構(gòu), 及有關(guān)術(shù)語(terminology), 先以一個
員工薪資檔(emp.dat ), 來加以介紹.

         A125  & Jenny  &100  &210   
         A341  & Dan    &110  &215
         P158  & Max    &130  &209
         P148  & John   &125  &220
         A123  & Linda  & 95  &210      
檔案中各字段依次為 員工ID, 姓名, 薪資率,及 實際工時. ID
中的第一碼為部門識別碼. ``A'',''P''分別表示``組裝''及``包裝''部門.
本小節(jié)著重于說明AWK程序的主要架構(gòu)及工作原理, 并對一些重要
的名詞輔以必要的解 釋. 由這部分內(nèi)容, 讀者可體會出AWK語言
的主要精神及AWK與其它語程序言的差異處. 為便于說明, 以條列
方式說明于后.

名詞定義

資料列: AWK從數(shù)據(jù)文件上讀取數(shù)據(jù)的基本單位.以上列檔案
emp.dat為例, AWK讀入的
   第一筆資料列是 "A125   Jenny  100  210"
   第二筆資料列是 "A341   Dan    110  215"
   一般而言, 一筆數(shù)據(jù)列相當(dāng)于數(shù)據(jù)文件上的一行資料.
   (參考 : 附錄 B 內(nèi)建變數(shù)``RS'' )

字段(Field) : 為資料列上被分隔開的子字符串.
          以資料列``A125   Jenny  100  210''為例,
第一欄 第二欄  第三欄 第四欄  ``A125''  ``Jenny''  100  210  
一般是以空格符來分隔相鄰的字段. ( 參考 : 附錄 D  內(nèi)建
變數(shù)``FS'' )

如何執(zhí)行AWK
于UNIX的命令列上鍵入諸如下列格式的指令: ( ``$''表Shell命令
列上的提示符號)
$awk 'AWK程序'  數(shù)據(jù)文件文件名  
則AWK會先編譯該程序, 然后執(zhí)行該程序來處理所指定的數(shù)據(jù)文件.
       (上列方式系直接把程序?qū)懺赨NIX的命令列上)
AWK程序的主要結(jié)構(gòu) :
      AWK程序中主要語法是 Pattern  { Actions}, 故常見之AWK
程序其型態(tài)如下 :
                Pattern1  { Actions1 }
                Pattern2  { Actions2 }
                ......
                Pattern3  { Actions3 }
   
  Pattern 是什么 ?
AWK 可接受許多不同型態(tài)的 Pattern. 一般常使用 ``關(guān)系判斷式'
(Relational expres sion) 來當(dāng)成 Pattern.
例如 :
    x  > 34 是一個Pattern, 判斷變量 x 與 34 是否存在 大于 的關(guān)系.

   x == y 是一個Pattern, 判斷變量 x 與變量 y 是否存在等于的關(guān)系.

    上式中 x  >34 , x == y 便是典型的Pattern.

    AWK 提供 C 語言中常見的關(guān)系操作數(shù)(Relational Operators) 如
     >, <,  >=,  <=, ==, !=.
    此外, AWK 還提供 ~ (match) 及 !~(not match) 二個關(guān)系操作數(shù)
(注一). 其用法與涵義如下:
若 A 表一字符串, B 表一 Regular Expression   
        A  ~ B 判斷 字符串A 中是否 包含   能合于(match)B式樣的
       子字符串.
        A !~ B 判斷 字符串A 中是否 未包含 能合于(match)B式樣的
       子字符串.
         
        例如 :

  ``banana'' ~ /an/ 整個是一個Pattern.
    因為``banana''中含有可match /an/的子字符串, 故此關(guān)系式
    成立(true),
    整個Pattern的值也是true.
相關(guān)細(xì)節(jié)請參考 附錄 A Patterns, 附錄 E Regular Expression
[注 一 :] 有少數(shù)AWK論著, 把 ~, !~ 當(dāng)成另一類的 Operator,
并不視為一種 Relational Operator. 本手冊中將這兩個操作數(shù)
當(dāng)成一種 Relational Operator.

Actions 是什么?
      Actions 是由許多AWK指令構(gòu)成. 而AWK的指令與 C 語言中的
指令十分類似.
        例如 :
AWK的 I/O指令 :  print, printf( ),  getline..
AWK的 流程控制指令 :  if(...){..} else{..},  while(...){...}...

(請參考 附錄 B --- ``Actions'' )
   

AWK 如何處理 Pattern { Actions } ?
      AWK 會先判斷(Evaluate) 該 Pattern 之值, 若 Pattern 判斷
(Evaluate)后之值為true(或不為0的數(shù)字,或不是空的字符串), 則 AWK
將執(zhí)行該 Pattern 所對應(yīng)的 Actions.
      反之, 若 Pattern 之值不為 true, 則AWK將不執(zhí)行該 Pattern
      所對應(yīng)的 Actions.
      例如 :  若AWK程序中有下列兩指令


              50   > 23  :        {print "Hello! The word!!" }
            "banana" ~ /123/  { print "Good morning !" }

AWK會先判斷 50  >23 是否成立. 因為該式成立, 所以AWK將印出
"Hello! The word!!". 而另一 Pattern 為 "banana" ~/123/, 因為
"banana" 內(nèi)未含有任何子字符串可 match /123/, 該 Pattern 之值為            
false, 故AWK將不會印出 "Good morning !"

   AWK 如何處理{ Actions } 的語法?(缺少Pattern部分)
        有時語法 Pattern { Actions }中, Pattern 部分被省略,
        只剩 {Actions}.
        這種情形表示 ``無條件執(zhí)行這個 Actions''.  

AWK 的字段變量
AWK 所內(nèi)建的字段變量及其涵意如下 :
字段變量  涵意  $0 為一字符串, 其內(nèi)容為目前 AWK 所讀入的資料列.  $1 代表 $0 上第一個字段的數(shù)據(jù).  $2 代表 $0 上第二欄個位的資料.  ...  其余類推  

讀入數(shù)據(jù)列時, AWK如何修正(update)這些內(nèi)建的字段變量

當(dāng) AWK 從數(shù)據(jù)文件中讀取一筆數(shù)據(jù)列時, AWK 會使用內(nèi)建變量
$0 予以記錄.
每當(dāng) $0 被異動時 (例如 : 讀入新的數(shù)據(jù)列 或 自行變更 $0,...)
      AWK 會立刻重新分析 $0 的字段情況, 并將 $0 上各字段的數(shù)據(jù)
用 $1, $2, ..予以記錄.


AWK的內(nèi)建變數(shù)(Built-in Variables)
AWK 提供了許多內(nèi)建變量, 使用者于程序中可使用這些變量
來取得相關(guān)信息.常見的內(nèi)建變數(shù)有 :
內(nèi)建變數(shù)  涵意  NF (Number of Fields)為一整數(shù), 其值表$0上所存在的字段數(shù)目.  NR (Number of Records)為一整數(shù), 其值表AWK已讀入的資料列數(shù)目.  FILENAMEAWK正在處理的數(shù)據(jù)文件文件名.  
例如 : AWK 從資料文件 emp.dat 中讀入第一筆資料列
  "A125  Jenny  100  210" 之后, 程序中:
   $0  之值將是 "A125   Jenny  100  210"
   $1  之值為 "A125"    $2  之值為 "Jenny"
   $3  之值為 100       $4  之值為 210
   NF 之值為 4         $NF   之值為 210
   NR 之值為 1         FILENAME 之值為 ``emp.dat''

AWK的工作流程 :
       執(zhí)行AWK時, 它會反復(fù)進(jìn)行下列四步驟.
      
  自動從指定的數(shù)據(jù)文件中讀取一筆數(shù)據(jù)列.
  自動更新(Update)相關(guān)的內(nèi)建變量之值. 如 :  NF, NR, $0...
  逐次執(zhí)行程序中 所有 的 Pattern { Actions } 指令.
  當(dāng)執(zhí)行完程序中所有 Pattern { Actions } 時, 若數(shù)據(jù)文件中還
        有未讀取的數(shù)據(jù), 則反復(fù)執(zhí)行步驟1到步驟4.

        AWK會自動重復(fù)進(jìn)行上述4個步驟, 使用者不須于程序中
撰寫這個循環(huán) (Loop).

打印檔案中指定的字段數(shù)據(jù)并加以計算

AWK 處理數(shù)據(jù)時, 它會自動從數(shù)據(jù)文件中一次讀取一筆記錄, 并會
將該數(shù)據(jù)切分成一個個的字段; 程序中可使用 $1, $2,... 直接取得
各個字段的內(nèi)容. 這個特色讓使用者易于用 AWK 撰寫 reformatter
來改變數(shù)據(jù)格式.

[ 范例 :] 以檔案 emp.dat 為例, 計算每人應(yīng)發(fā)工資并打印報表.
[ 分析 :] AWK 會自行一次讀入一列數(shù)據(jù), 故程序中僅需告訴
AWK 如何處理所讀入的數(shù)據(jù)列.

      執(zhí)行如下命令 : ( $ 表UNIX命令列上的提示符號 )
      
        awk '{ print  $2, $3 * $4  }' emp.dat
     執(zhí)行結(jié)果如下 :
     屏幕出現(xiàn)  :

                 Jenny 21000
                 Dan 23650
                 Max 27170
                 John 27500
                 Linda 19950


     說 明 :  
        
UNIX命令列上, 執(zhí)行AWK的語法為:
      awk  'AWK程序'  欲處理的資料文件文件名.
      本范例中的 程序部分 為 {print $2, $3 * $4}.
      把程序置于命令列時, 程序之前后須以  ' 括住.
emp.dat 為指定給該程序處理的數(shù)據(jù)文件文件名.
           
本程序中使用 :  Pattern { Actions } 語法.
Pattern Actions   print $2, $3 * $4  
Pattern 部分被省略, 表無任何限制條件. 故AWK讀入每筆資料列
后都將無條件執(zhí)行這個 Actions.
print為AWK所提供的輸出指令, 會將數(shù)據(jù)輸出到stdout(屏幕).
     print 的參數(shù)間彼此以 ``{ ,}'' 隔開, 印出數(shù)據(jù)時彼此間會以空白
     隔開.
(參考 附錄 D  內(nèi)建變量OFS)
將上述的 程序部分 儲存于檔案  pay1.awk 中. 執(zhí)行命令時再指定AWK程序文件 之文件名. 這是執(zhí)行AWK的另一種方式, 特別適用于程
式較大的情況, 其語法如下:
$awk -f AWK程序文件名 數(shù)據(jù)文件文件名

故執(zhí)行下列兩命令,將產(chǎn)生同樣的結(jié)果.

$awk -f  pay1.awk  emp.dat
$awk ' { print  $2, $3 * $4 } '  emp.dat
      
讀者可使用``-f''參數(shù),讓AWK主程序使用其它僅含 AWK函數(shù) 的
檔案中的函數(shù)
其語法如下:
awk  -f AWK主程序文件名 -f AWK函數(shù)文件名 數(shù)據(jù)文件文件名
    (有關(guān) AWK 中函數(shù)之宣告與使用于 7.4 中說明)                             
AWK中也提供與 C 語言中類似用法的 printf() 函數(shù). 使用
該函數(shù)可進(jìn)一步控制數(shù)據(jù)的輸出格式.

編輯另一個AWK程序如下, 并取名為  pay2.awk
            
{ printf("\%6s   Work hours: %3d  Pay: %5d\", $2,\$3, $3* $4) }

執(zhí)行下列命令
         
$awk -f  pay2.awk   emp.dat
               
執(zhí)行結(jié)果屏幕出現(xiàn):
                Jenny   Work hours: 100 Pay: 21000
                   Dan   Work hours: 110 Pay: 23650
                   Max   Work hours: 130 Pay: 27170
                  John   Work hours: 125 Pay: 27500
                 Linda   Work hours:  95 Pay: 19950


選印合乎指定條件的記錄
Pattern { Action }為AWK中最主要的語法. 若某Pattern之值為真則執(zhí)行它后方的 Action. AWK中常使用``關(guān)系判斷式'' (Relational Expression)來當(dāng)成 Pattern.
AWK中除了>, <, ==, != ,...等關(guān)系操作數(shù)( Relational Operators )外,另外提供 ~(match),!~(Not Match) 二個關(guān)系操作數(shù). 利用這兩個操作數(shù), 可判斷某字符串是否包含能符合所指定 Regular Expression 的子字符串. 由于這些特性, 很容易使用AWK來撰寫需要字符串比對, 判斷的程序. [ 范例 :] 承上例,
組裝部門員工調(diào)薪5%,(組裝部門員工之ID.系以``A''開頭)
所有員工最后之薪資率若仍低于100, 則以100計.
撰寫AWK程序行印新的員工薪資率報表.
[分析 ] : 這個程序須先判斷所讀入的數(shù)據(jù)列是否合于指定條件, 再進(jìn)行某些動作.AWK中 Pattern { Actions } 的語法已涵蓋這種 `` if ( 條件 ) { 動作} ''的架構(gòu). 編寫如下之程序, 并取名 adjust1.awk
$1 ~ /^A.*/ { $3 *= 1.05 } $3<100 { $3 = 100 }

{ printf("%s %8s %d\n", $1, $2, $3)} 執(zhí)行下列命令 :

$awk -f adjust1.awk emp.dat

結(jié)果如下 : 屏幕出現(xiàn) :
A125 Jenny 105
A341 Dan 115
P158 Max 130
P148 John 125
A123 Linda 100
說 明 :

AWK的工作程序是: 從數(shù)據(jù)文件中每次讀入一筆數(shù)據(jù)列, 依序執(zhí)行完程序中所有的 Pattern{ Action }指令 Pattern Actions  
Pattern                                Actions                                       
$1~/^A.*/                                { $3 *= 1.05 }                                       
$3 < 100                                { $3 = 100 }                                       
                                {printf("%s%8s%d\n",$1,$2,$3)}                                       

再從數(shù)據(jù)文件中讀進(jìn)下一筆記錄繼續(xù)進(jìn)行處理.
第一個 Pattern { Action }是: $1 ~ /^A.*/ { $3 *= 1.05 } $1 ~ /^A.*/ 是一個Pattern, 用來判斷該筆資料列的第一欄是否包含%以``A''開頭的子字符串. 其中 /^A.*/ 是一個Regular Expression, 用以表示任何以``A''開頭的字符串. (有關(guān) Regular Expression 之用法 參考 附錄 E ).
Actions 部分為 $3 *= 1.05 $3 *= 1.05 與 $3 = $3 * 1.05 意義相同. 運(yùn)算子``*='' 之用法則與 C 語言中一樣. 此后與 C 語言中用法相同的運(yùn)算子或語法將不予贅述.


第二個 Pattern { Actions } 是: $3 <100 {$3 = 100 } 若第三欄的數(shù)據(jù)內(nèi)容(表薪資率)小于100, 則調(diào)整為100.
第三個 Pattern { Actions } 是: {printf("%s %-8s %d\n",$1, $2, $3 )} 省略了Pattern(無條件執(zhí)行Actions), 故所有數(shù)據(jù)列調(diào)整后的數(shù)據(jù)都將被印出.


AWK 中數(shù)組的特色

AWK程序中允許使用字符串當(dāng)做數(shù)組的注標(biāo)(index). 利用
這個特色十分有助于資料統(tǒng)計工作.(使用字符串當(dāng)注標(biāo)的數(shù)組稱為
Associative  Array)   
     首先建立一個數(shù)據(jù)文件, 并取名為 reg.dat. 此為一學(xué)生注冊的
資料文件; 第一欄為學(xué)生姓名, 其后為該生所修課程.           
              Mary      O.S.         Arch.         Discrete
              Steve     D.S.         Algorithm    Arch.
              Wang    Discrete   Graphics     O.S.
              Lisa       Graphics   A.I.            
              Lily        Discrete   Algorithm   
AWK中數(shù)組的特性

使用字符串當(dāng)數(shù)組的注標(biāo)(index).
使用數(shù)組前不須宣告數(shù)組名及其大小.
例如 : 希望用數(shù)組來記錄 reg.dat 中各門課程的修課人數(shù).
          這情況,有二項信息必須儲存 :
         (a) 課程名稱, 如 : ``O.S.'',``Arch.''.. ,共有哪些課程事前
并不明確.
         (b)各課程的修課人數(shù). 如 : 有幾個人修``O.S.''
在AWK中只要用一個數(shù)組就可同時記錄上列信息. 其方法如下 :
使用一個數(shù)組 Number[ ]  :

以課程名稱當(dāng) Number[ ] 的注標(biāo).
以 Number[ ] 中不同注標(biāo)所對映的元素代表修課人數(shù).

例如 :
有2個學(xué)生修 ``O.S.'', 則以 Number[``O.S.''] = 2 表之.
若修``O.S.''的人數(shù)增加一人,
則 Number[``O.S.''] = Number[``O.S.''] + 1
或 Number["O.S."]++ .
如何取出數(shù)組中儲存的信息
以 C 語言為例, 宣告 int  Arr[100]; 之后, 若想得知 Arr[ ]中所
儲存的數(shù)據(jù), 只須用一個循環(huán), 如 :
for(i=0; i<00; i++) printf("%d\n", Arr);
即可. 上式中 :
數(shù)組 Arr[ ] 的注標(biāo) : 0, 1, 2,..., 99
數(shù)組 Arr[ ] 中各注標(biāo)所對應(yīng)的值 : Arr[0], Arr[1],...Arr[99]
但 AWK 中使用數(shù)組并不須事先宣告. 以剛才使用的 Number[ ] 而言,
程序執(zhí)行前, 并不知將來有哪些課程名稱可能被當(dāng)成 Number[ ] 的
注標(biāo).
AWK 提供了一個指令, 藉由該指令A(yù)WK會自動找尋數(shù)組中使用過
的所有注標(biāo). 以 Number[ ] 為例, AWK將會找到 ``O.S.'', ``Arch.''",...
使用該指令時, 須指定所要找尋的數(shù)組, 及一個變量. AWK會使用
該的變量來記錄從數(shù)組中找到的每一個注標(biāo). 例如
for(course in Number){....}
指定用 course 來記錄 AWK 從Number[ ] 中所找到
的注標(biāo). AWK每找到一個注標(biāo)時, 就用course記錄該注標(biāo)之值且
執(zhí)行{....}中之指令. 藉由這個方式便可取出數(shù)組中儲存的信息.
(詳見下例)

范例 : 統(tǒng)計各科修課人數(shù),并印出結(jié)果.
          建立如下程序,并取名為 course.awk:
         {  for( i=2; i
      END{for(coursein Number)
      printf("\%-10s %d\n", course, Number[course] )
         }

  執(zhí)行下列命令 :
  awk  -f   course.awk    reg.dat
  執(zhí)行結(jié)果如下 :

                  Discrete    3
                  D.S.          1
                  O.S.          2
                  Graphics   2
                  A.I.           1
                  Arch.        2
                  Algorithm  2
                                       
      說  明 :
        
這程序包含二個Pattern { Actions }指令.

Pattern        Actions
        {for( i=2; i< NF; i++) Number[$i]++ }
END        { for( course in Number) printf("\%-10s \%d\n", course, Number[course] )}

第一個Pattern { Actions }指令中省略了Pattern 部分. 故隨著
每筆數(shù)據(jù)列的讀入其Actions部分將逐次無條件被執(zhí)行.
以AWK讀入第一筆資料 `` Mary  O.S.  Arch. Discrete" 為例,
因為該筆數(shù)據(jù) NF = 4(有4個字段), 故該 Action 的for Loop中
i = 2,3,4.

i        $i                        最初 Number[$i]                                        Number[$i]++ 之后               
2        ``O.S.''                        AWK default Number[``O.S'']=0                                        1               
3        ``Arch.''                        AWK default Number[``Arch'']=0                                        1               
4        ``Discrete''                        AWK default Number[``Discrete'']=0                                        1


第二個 Pattern { Actions }指令中
    * { END}為AWK之保留字, 為{ Pattern}之一種.
    * { END}成立(其值為true)的條件是 :[0.3cm]
     ``AWK處理完所有數(shù)據(jù), 即將離開程序時.
      平常讀入資料列時,  END并不成立, 故其后的Actions
并不被執(zhí)行;
      唯有當(dāng)AWK讀完所有數(shù)據(jù)時, 該Actions才會被執(zhí)行 ( 注意,
不管數(shù)據(jù)列有多少筆, END僅在最后才成立, 故該Actions僅被執(zhí)行
一次.)
     { BEGIN} 與 { END} 有點(diǎn)類似, 是AWK中另一個保留的{Pattern}.
      唯一不同的是 :
``以 { BEGIN 為 Pattern 的 Actions} 于程序一開始執(zhí)行時, 被執(zhí)行
一次.''
NF 為AWK的內(nèi)建變量, 用以表示AWK正處理的數(shù)據(jù)計列中,
所包含的字段個數(shù).
               
AWK程序中若含有以 $ 開頭的自定變量, 都將以如下方式解釋 :
以 i= 2 為例, $i = $2 表第二個字段數(shù)據(jù). ( 實際上, $  在 AWK 中
為一操作數(shù)(Operator), 用以取得字段數(shù)據(jù).)

AWK 程序中使用 Shell 命令

AWK程序中允許呼叫Shell指令. 并提供pipe解決AWK與系統(tǒng)間
數(shù)據(jù)傳遞的問題. 所以AWK很容易使用系統(tǒng)資源. 讀者可利用這個
特色來撰寫某些適用的系統(tǒng)工具.
      范例 : 寫一AWK程序來打印出線上人數(shù).  
          將下列程序建文件, 命名為 count.awk

       BEGIN  {
                    while ( "who" | getline ) n++
                    print n
                    }
       并執(zhí)行下列命令 :
       awk { -f} count.awk
       執(zhí)行結(jié)果將會印出目前在線人數(shù)
       說 明 :

AWK 程序并不一定要處理資料文件. 以本例而言, 僅輸入程序
檔count.awk, 未輸入任何數(shù)據(jù)文件.
BEGIN 和 END 同為AWK中之種一 Pattern. 以 BEGIN 為
Pattern之Actions,只有在AWK開始執(zhí)行程序,尚未開啟任何輸入
檔前, 被執(zhí)行一次.(注意: 只被執(zhí)行一次 )
``{ |}'' 為 AWK 中表示 pipe的符號. AWK 把 pipe之前的字符串
''who''當(dāng)成Shell上的命令, 并將該命令送往Shell執(zhí)行, 執(zhí)行的結(jié)果
(原先應(yīng)于屏幕印出者)則藉由pipe送進(jìn)AWK程序中.                  
getline為AWK所提供的輸入指令.
          其語法如下 :

語法        由何處讀取數(shù)據(jù)        數(shù)據(jù)讀入后置于
getline var < file        所指定的 file        變量 var(var省略時,表示置于$0)
getline var        pipe        變量 var(var省略時,表示置于$0)
getline var        見 注一        變量 var(var省略時,表示置于$0)

注一 : 當(dāng) Pattern 為 BEGIN 或 END 時, getline 將由 stdin 讀取數(shù)據(jù),
否則由AWK正處理的數(shù)據(jù)文件上讀取數(shù)據(jù).
        getline 一次讀取一行數(shù)據(jù),
                  若讀取成功則return 1,
                  若讀取失敗則return -1,
                  若遇到檔案結(jié)束(EOF), 則return 0;
             本程序使用 getline 所 return 的數(shù)據(jù) 來做為 while 判斷
循環(huán)停止的條件,某些AWK版本較舊,并不容許使用者改變 $0 之值.
這種版的 AWK 執(zhí)行本程序時會產(chǎn)生 Error, 讀者可于 getline 之后
置上一個變量 (如此, getline 讀進(jìn)來的數(shù)據(jù)便不會被置于 $0 ),
或直接改用gawk便可解決.

AWK 程序的應(yīng)用實例

本節(jié)將示范一個統(tǒng)計上班到達(dá)時間及遲到次數(shù)的程序.

這程序每日被執(zhí)行時將讀入二個檔案 :
            員工當(dāng)日到班時間的數(shù)據(jù)文件 ( 如下列之 arr.dat )
            存放員工當(dāng)月遲到累計次數(shù)的檔案.
  當(dāng)程序執(zhí)行執(zhí)完畢后將更新第二個檔案的數(shù)據(jù)(遲到次數(shù)), 并打印
當(dāng)日的報表.這程序?qū)⒎殖上铝袛?shù)小節(jié)逐步完成, 其大綱如下 :
     
[7.1] 于到班資料文件 {arr.dat} 之前端增加一列抬頭
"ID  Number    Arrvial Time", 并產(chǎn)生報表輸出到檔案today_rpt1 中''
< 在AWK中如何將數(shù)據(jù)輸出到檔案  >
[7.2]將 {today\_rpt1} 上之?dāng)?shù)據(jù)按員工代號排序, 并加注執(zhí)行當(dāng)日
              之日期; 產(chǎn)生檔案 today_rpt2
             < AWK中如何運(yùn)用系統(tǒng)資源及AWK中Pipe之特性 >
[7.3]< 將AWK程序包含在一個shell script檔案中>
[7.4] 于 today_rpt2 每日報表上, 遲到者之前加上"*", 并加注當(dāng)日
平均到班時間; 產(chǎn)生檔案 today_rpt3

[7.5] 從檔案中讀取當(dāng)月遲到次數(shù), 并根據(jù)當(dāng)日出勤狀況更新遲到累計數(shù).
< 使用者于AWK中如何讀取檔案數(shù)據(jù)  >
某公司其員工到勤時間檔如下, 取名為 {arr.dat}. 檔案中第一欄為
員工代號, 第二欄為到達(dá)時間. 本范例中, 將使用該檔案為數(shù)據(jù)文件.

                1034    7:26
                1025    7:27
                1101    7:32
                1006    7:45
                1012    7:46
                1028    7:49
                1051    7:51
                1029    7:57
                1042    7:59
                1008    8:01
                1052    8:05
                1005    8:12

將數(shù)據(jù)直接輸出到檔案

AWK中并未提供如 C 語言中之fopen() 指令, 也未有fprintf()
檔案輸出之指令. 但AWK中任何輸出函數(shù)之后皆可藉由使用與
UNIX 中類似的 I/O  Redirection , 將輸出的數(shù)據(jù) Redirect 到指定
的檔案; 其符號仍為 > (輸出到一個新產(chǎn)生的檔案) 或 >> ( append
輸出的數(shù)據(jù)到檔案末端 ).

[例 :]于到班資料文件 arr.dat 之前端增加一列抬頭如下 :
"ID  Number    Arrival Time", 并產(chǎn)生報表輸出到檔案 today_rpt1中      

建立如下檔案并取名為reformat1.awk

BEGIN  { print `` ID  Number  Arrival Time''  > ``today_rpt1''
              print ``==========================='' > ``today_rpt1''
             }  

             { printf("    %s  %s\n", $1,$2 )  > "today_rpt1" }      執(zhí)行:

              $awk  -f reformat1.awk   arr.dat

       執(zhí)行后將產(chǎn)生檔案 today\_rpt1, 其內(nèi)容如下 :
        ID  Number   Arrival Time
       ============================
           1034         7:26
           1025         7:27
           1101         7:32
           1006         7:45
           1012         7:46
           1028         7:49
           1051         7:51
           1029         7:57
           1042         7:59
           1008         8:01
           1052         8:05
           1005         8:12
      說 明 :

AWK程序中, 文件名稱 today_rpt1 之前后須以" 括住, 表示
today_rpt1 為一字符串常數(shù). 若未以"括住, 則 today_rpt1 將
被AWK解釋為一個變量名稱.
在AWK中任何變量使用之前, 并不須事先宣告. 其初始值為空字符串
(Null string) 或 0.因此程序中若未以 " 將 today_rpt1 括住,
則 today_rpt1 將是一變量, 其值將是空字符串, 這會于執(zhí)行時
造成錯誤(Unix 無法幫您開啟一個以Null String為檔名的檔案).

* 因此在編輯 AWK程序時, 須格外留心. 因為若敲錯變量名稱,
  AWK在編譯程序時會認(rèn)為是一新的變量, 并不會察覺. 如此
  往往會造成 RuntimeError.

BEGIN 為AWK的保留字, 是 Pattern 的一種.
     以 BEGIN 為 Pattern 的 Actions 于AWK程序剛被執(zhí)行尚未讀取
     數(shù)據(jù)時被執(zhí)行一次, 此后便不再被執(zhí)行.
讀者或許覺得本程序中的I/O Redirection符號應(yīng)使用 `` >>''
(append)而非 `` >''.
   \index{ { >} } \index{ { >>} }
  • 本程序中若使用 ``>'' 將數(shù)據(jù)重導(dǎo)到 today_rpt1, AWK
    第一次執(zhí)行該指令時會產(chǎn)生一個新檔 today_rpt1, 其后再
    執(zhí)行該指令時則把數(shù)據(jù)append到today_rpt1文件末, 并非每執(zhí)行
    一次就重開一個新檔.
  • 若采用">>"其差異僅在第一次執(zhí)行該指令時, 若已存在
    today_rpt1則 AWK 將直接把數(shù)據(jù)append在原檔案之末尾.
  • 這一點(diǎn), 與UNIX中的用法不同.

    AWK 中如何利用系統(tǒng)資源

    AWK程序中很容易使用系統(tǒng)資源. 這包括于程序中途叫用 Shell
    命令來處理程序中的部分?jǐn)?shù)據(jù); 或于呼叫 Shell 命令后將其產(chǎn)生
    之結(jié)果交回 AWK 程序(不需將結(jié)果暫存于某個檔案). 這過程乃是
    藉由 AWK 所提供的 pipe (雖然有些類似 Unix 中的 pipe, 但特性
    有些不同),及一個從 AWK 中呼叫 Unix 的 Shell command 的語法
    來達(dá)成.
    [例 :] 承上題, 將數(shù)據(jù)按員工ID排序后再輸出到檔案 today_rpt2,
    并于表頭附加執(zhí)行時的日期.
      分 析 :

    AWK 提供與 UNIX 用法近似的 pipe, 其記號亦為 ``|''. 其用法及
    涵意如下 :
    AWK程序中可接受下列兩語法 :
    [a. 語法]  AWK output 指令 | ``Shell 接受的命令''
                ( 如 : print $1,$2 | "sort +1n" )  
         
    [b. 語法] ``Shell 接受的命令'' |AWK input 指令      
                 ( 如 : "ls " | getline)            
          
    注 : AWK input 指令只有   getline 一個.
    AWK output 指令有   print, printf() 二個.         

    于 a 語法中, AWK所輸出的數(shù)據(jù)將轉(zhuǎn)送往 Shell, 由 Shell 的
    命令進(jìn)行處理.以上例而言, print 所印出的數(shù)據(jù)將經(jīng)由 Shell 命令
    ``sort +1n'' 排序后再送往屏幕(stdout).
    上列AWK程序中, ``print$1, $2'' 可能反復(fù)執(zhí)行很多次, 其印出的
    結(jié)果將先暫存于 pipe 中,
    等到該程序結(jié)束時, 才會一并進(jìn)行 ``sort +1n''.
    須注意二點(diǎn) : 不論 print \$1, \$2 被執(zhí)行幾次,
           ``sort +1n'' 之 執(zhí)行時間是  ``AWK程序結(jié)束時'',
           ``sort +1n'' 之 執(zhí)行次數(shù)是  ``一次''.
       于 b 語法中, AWK將先叫用 Shell 命令. 其執(zhí)行結(jié)果將經(jīng)由
    pipe 送入AWK程序以上例而言, AWK先令 Shell 執(zhí)行 ``ls'',
    Shell 執(zhí)行后將結(jié)果存于 pipe, AWK指令 getline再從 pipe 中讀取
    資料.
    使用本語法時應(yīng)留心 : 以上例而言
    AWK ``立刻''呼叫 Shell 來執(zhí)行 ``ls'', 執(zhí)行次數(shù)是一次.
    getline 則可能執(zhí)行多次(若pipe中存在多行數(shù)據(jù)).      
       除上列 a, b 二語法外, AWK程序中它處若出現(xiàn)像 "date", "cls", "ls"...
            等字符串, AWK只當(dāng)成一般字符串處理之.


        建立如下檔案并取名為 reformat2.awk

        #  程序 reformat2.awk  
        #  這程序用以練習(xí)AWK中的pipe
    BEGIN {
                 "date"  |  getline # Shell 執(zhí)行 ``date''. getline 取得結(jié)果
                 并以$0記錄
                  print " Today is " , $2, $3 >"today_rpt2"
                  print "=========================" > "today_rpt2"
                  print `` ID  Number Arrival Time'' >``today_rpt2''
                  close( "today_rpt2" )
          }

       {printf( "%s  \%s\n", $1 ,$2 )"sort +2n >>today_rpt2"}
    執(zhí)行如下命令:
    awk -f   reformat2.awk  arr.dat

    執(zhí)行后, 系統(tǒng)會自動將 sort 后的數(shù)據(jù)加( Append; 因為使用 `` >>'')
    到檔案 today_rpt2末端.  today_rpt2 內(nèi)容如下 :
             Today is  Sep 17
         =========================
          ID Number   Arrival Time
             1005         8:12
             1006         7:45
             1008         8:01
             1012         7:46
             1025         7:27
             1028         7:49
             1029         7:57
             1034         7:26
             1042         7:59
             1051         7:51
             1052         8:05
             1101         7:32
                  
      說 明 :

    AWK程序由三個主要部分構(gòu)成 :
         [ i.]    Pattern { Action} 指令
         [ ii.]   函數(shù)主體.  例如 : function double( x ){ return 2*x }
        (參考第11節(jié) Recursive Program )
         [ iii.]  Comment       ( 以 # 開頭識別之 )

    AWK 的輸入指令 getline, 每次讀取一列數(shù)據(jù). 若getline之后
    未接任何變量, 則所讀入之資料將以$0 紀(jì)錄, 否則以所指定的變量
    儲存之.
    [ 以本例而言] :
          執(zhí)行 "date" | getline 后,
           $0 之值為 "Wed Aug 17 11:04:44 EAT 1994"
      當(dāng) $0 之值被更新時, AWK將自動更新相關(guān)的內(nèi)建變量, 如 :
    $1,$2,..,NF.故 $2 之值將為"Aug", $3之值將為"17".
      (有少數(shù)舊版之AWK不允許即使用者自行更新(update)$0之值,或者
    update$0時,它不會自動更新 $1,$2,..NF. 這情況下, 可改用gawk,
    或nawk. 否則使用者也可自行以AWK字符串函數(shù)split()來分隔$0上
    的資料)

      本程序中 printf() 指令會被執(zhí)行12次( 因為有arr.dat中有12筆
    數(shù)據(jù)), 但讀者不用 擔(dān)心數(shù)據(jù)被重復(fù)sort了12次. 當(dāng)AWK結(jié)束該程序
    時才會 close 這個 pipe , 此時才將這12筆數(shù)據(jù)一次送往系統(tǒng),
    并呼叫 "sort +2n >> today_rpt2" 處理之.
    AWK提供另一個叫用Shell命令的方法, 即使用AWK函數(shù)
                system("shell命令"

            例如 :
                  awk '
                  BEGIN{
                          system("date  > date.dat"
                          getline  <date.dat
                          print "Today is ", $2, $3
                        }
                    '

            但使用 system( "shell 命令" ) 時, AWK無法直接將執(zhí)行中的
    部分?jǐn)?shù)據(jù)輸出給Shell 命令. 且 Shell 命令執(zhí)行的結(jié)果也無法直接
    輸入到AWK中.


    執(zhí)行 AWK 程序的幾種方式

    本小節(jié)中描述如何將AWK程序直接寫在 shell script 之中. 此后
    使用者執(zhí)行 AWK 程序時, 就不需要每次都鍵入
    `` awk -f program  datafile''
    script 中還可包含其它 Shell 命令, 如此更可增加執(zhí)行過程的自動化.
    建立一個簡單的AWK程序 mydump.awk, 如下 :
                      {print}
    這個程序執(zhí)行時會把數(shù)據(jù)文件的內(nèi)容 print 到屏幕上( 與cat功用類似 ).
    print 之后未接任何參數(shù)時, 表示 ``print $0''.
    若欲執(zhí)行該AWK程序, 來印出檔案 today_rpt1 及 today_rpt2 的內(nèi)容時,
    必須于 UNIX 的命令列上執(zhí)行下列命令 :

    方式一 awk   -f  mydump.awk  today_rpt1 today_rpt2
    方式二 awk ' print '  today_rpt1  today_rpt2第二種方式系將AWK
    程序直接寫在 Shell 的命令列上, 這種方式僅適合較短的AWK程序.
    方式三 建立如下之 shell script, 并取名為 mydisplay,
                awk '     # 注意 , awk 與 ' 之間須有空白隔開
                     {print}                                   
                     ' $*  # 注意 , ' 與 $* 之間須有空白隔開

    執(zhí)行 mydisplay 之前, 須先將它改成可執(zhí)行的檔案(此步驟
    往后不再贅述). 請執(zhí)行如下命令:
    $ chmod  +x mydisplay
    往后使用者就可直接把 mydisplay 當(dāng)成指令, 來display任何檔案.
    例如 :

             $ mydisplay  today_rpt1 today_rpt2

    說 明 :

    在script檔案 mydisplay 中, 指令``awk''與第一個  '
    之間須有空格(Shell中并無`` awk' ''指令).
    第一個  ' 用以通知 Shell 其后為AWK程序.
    第二個  ' 則表示 AWK 程序結(jié)束.
    故AWK程序中一律以"括住字符串或字符, 而不使用 ' ,
    以免Shell混淆.

    $* 為 shell script中之用法, 它可用以代表命令列上 ``mydisplay
    之后的所有參數(shù)''.
    例如執(zhí)行 :
               $ mydisplay today_rpt1 today_rpt2
    事實上 Shell 已先把該指令轉(zhuǎn)換成 :
        awk '
                 { print}
            '   today_rpt1  today_rpt2
    本例中, $* 用以代表 ``today_rpt1 today_rpt2''. 在Shell的語法中,
    可用 $1 代表第一個參數(shù), $2 代表第二個參數(shù). 當(dāng)不確定命令列上的
    參數(shù)個數(shù)時, 可使用 $* 表之.
    AWK命令列上可同時指定多個數(shù)據(jù)文件.  
      以awk  -f dump.awk  today_rpt1 today_rpt2hf 為例
      AWK會先處理today_rpt1, 再處理 today_rpt2. 此時若檔案
      無法開啟, 將造成錯誤.
      例如: 未存在檔案"file_no_exist", 則執(zhí)行 :
      awk  -f dump.awk  file_no_exit
      將產(chǎn)生Runtime Error(無法開啟檔案).
      但某些AWK程序 ``僅'' 包含以 BEGIN 為Pattern的指令. 執(zhí)行這種
      AWK程序時, AWK并不須開啟任何數(shù)據(jù)文件.此時命令列上若指定
    一個不存在的數(shù)據(jù)文件,并不會產(chǎn)生 ``無法開啟檔案''的錯誤.(事實上
    AWK并未開啟該檔案)
    例如執(zhí)行:
    awk 'BEGIN {print "Hello,World!!"} ' file_no_exist
    該程序中僅包含以 BEGIN 為 Pattern 之 Pattern {actions}, AWK
    執(zhí)行時并不會開啟任何數(shù)據(jù)文件; 故不會因不存在檔案file_no_exit而
    產(chǎn)生 `` 無法開啟檔案''的錯誤.  
    AWK會將 Shell 命令列上AWK程序(或 -f 程序文件名)之后的所有
    字符串, 視為將輸入AWK進(jìn)行處理的數(shù)據(jù)文件文件名.
    若執(zhí)行AWK的命令列上 ``未指定任何數(shù)據(jù)文件文件名'', 則將stdin視為
    輸入之?dāng)?shù)據(jù)來源, 直到輸入end of file( Ctrl-D )為止.
    讀者可以下列程序自行測試, 執(zhí)行如下命令 :
           $awk  -f dump.awk   (未接任何資料文件文件名)

           $ mydisplay         (未接任何資料文件文件名)
    將會發(fā)現(xiàn) : 此后鍵入的任何數(shù)據(jù)將逐行復(fù)印一份于屏幕上. 這情況
    不是機(jī)器當(dāng)機(jī) ! 是因為AWK程序正處于執(zhí)行中. 它正按程序指示,
    將讀取數(shù)據(jù)并重新dump一次; 只因執(zhí)行時未指定數(shù)據(jù)文件文件名, 故AWK
    便以stdin(鍵盤上的輸入)為數(shù)據(jù)來源.
    讀者可利用這個特點(diǎn), 設(shè)計可與AWK程序interactive talk的程序.

    改變 AWK 切割字段的方式 & 使用者定義函數(shù)
          
    AWK不僅能自動分割字段, 也允許使用者改變其字段切割方式以
    適應(yīng)各種格式之需要. 使用者也可自定函數(shù), 若有需要可將該函數(shù)
    單獨(dú)寫成一個檔案,以供其它AWK程序叫用.

    范例 : 承接 6.2 的例子, 若八點(diǎn)為上班時間, 請加注 ``*''于遲到記錄
    之前, 并計算平均上班時間.

    分  析:

      因八點(diǎn)整到達(dá)者,不為遲到, 故僅以到達(dá)的小時數(shù)做判斷是不夠的;
    仍應(yīng)參考到達(dá)時的分鐘數(shù). 若 ``將到達(dá)時間轉(zhuǎn)換成以分鐘為單位'',
    不僅易于判斷是否遲到, 同時也易于計算到達(dá)平均時間.

    到達(dá)時間($2)的格式為 dd:dd 或 d:dd; 數(shù)字當(dāng)中含有一個 ":".
    但文數(shù)字交雜的數(shù)據(jù)AWK無法直接做數(shù)學(xué)運(yùn)算. (注: AWK中字符串
    "26"與數(shù)字26, 并無差異, 可直接做字符串或數(shù)學(xué)運(yùn)算, 這是AWK重要
    特色之一. 但AWK對文數(shù)字交雜的字符串無法正確進(jìn)行數(shù)學(xué)運(yùn)算).

    解決之方法 :

    方法一.
    對到達(dá)時間($2) d:dd 或 dd:dd 進(jìn)行字符串運(yùn)算,分別取出到達(dá)的小時數(shù)
    及分鐘數(shù).
               
    首先判斷到達(dá)小時數(shù)為一位或兩位字符,再呼叫函數(shù)分別截取分鐘數(shù)
    及小時數(shù).

    此解法需使用下列AWK字符串函數(shù):

    length( 字符串 ) : 傳回該字符串之長度.
    substr( 字符串,起始位置 ,長度 ) :傳回從起始位置起, 指定長度
    之子字符串. 若未指定長度, 則傳回起始位置到自串末尾之子字符串.
    所以:

    小時數(shù) = substr( $2, 1,  length($2) - 3 )
    分鐘數(shù) = substr( $2, length($2) - 2 )

    [方法二]  
                     改變輸入列字段的切割方式, 使AWK切割字段后分別將
                     小時數(shù)及分鐘數(shù)隔開于二個不同的字段.
                     字段分隔字符 FS (field seperator) 是AWK的內(nèi)建變數(shù),
                     其默認(rèn)值是空白及tab. AWK每次切割字段時都會先參考
                      FS 的內(nèi)容. 若把":"也當(dāng)成分隔字符, 則AWK 便能自動把
                     小時數(shù)及分鐘數(shù)分隔成不同的字段.
                     故令
       FS = "[ \t:]+" (注 : [ \t:]+ 為一Regular Expression )

    Regular Expression 中使用中括號 [ ...  ] 表一字符集合,
    用以表示任意一個位于兩中括號間的字符.
    故可用``[ \t:]''表示 一個 空白 , tab 或 ``:''
    Regular Expression中使用 ``+'' 形容其前方的字符可出現(xiàn)一次
    或一次以上.
    故 ``[ \t:]+'' 表示由一個或多個 ``空白, tab 或 : '' 所組成的字符串.

    設(shè)定 FS =''[ \t:]+'' 后, 資料列如 : ``1034    7:26'' 將被分割成3個字段

    第一欄        第二欄        第三欄
    $1         $2         $3
    1034        7        26
    getline var        見 注一        變量 var(var省略時,表示置于$0)

    明顯地, AWK程序中使用方法一比方法二更簡潔方便. 本范例中采用
    方法二,也藉此示范改變字段切割方式之用途.

    編寫AWK程序 reformat3, 如下 :
    awk '
    BEGIN  {
                {FS= "[ \t:]+"  #改變字段切割的方式            
                "date"  | getline  # Shell 執(zhí)行 ``date''. getline 取得結(jié)果以$0紀(jì)錄
                 print " Today is " ,$2, $3  > "today_rpt3"
                 print "=========================">"today_rpt3"
                 print `` ID  Number Arrival Time'' > ``today_rpt3''
                 close( "today_rpt3" )
                }

        {
       #已更改字段切割方式, $2表到達(dá)小時數(shù), $3表分鐘數(shù)
    arrival = HM_to_M($2, $3)
    printf("  %s   %s:%s %s\n", $1,$2, $3
    , arrival  > 480 ? "*": " "  ) | "sort +0n>>today_rpt3"
    total += arrival
    END {
                close("today_rpt3"   #參考本節(jié)說明 5
                close("sort +0n  >> today_rpt3"
               printf(" Average arrival time : %d:%d\n",
                      total/NR/60, (total/NR)%60 ) >> "today_rpt3"
             }
          function HM_to_M( hour, min ){
                  return  hour*60 + min
                 }
          ' $*
    并執(zhí)行如下指令 :
    $ reformat3  arr.doc
       執(zhí)行后,檔案 today_rpt3 的內(nèi)容如下:
            Today is  Sep 21
         =========================
          ID  Number Arrival Time
              1005      8:12  *
              1006      7:45   
              1008      8:01  *
              1012      7:46   
              1025      7:27   
              1028      7:49   
              1029      7:57   
              1034      7:26   
              1042      7:59   
              1051      7:51   
              1052      8:05  *
              1101      7:32   
          Average arrival time : 7:49
    {verbatim}  
      說 明 :

    AWK 中亦允許使用者自定函數(shù). 函數(shù)定義方式請參考本程序,
    function 為 AWK 的保留字.
    HM_to_M( ) 這函數(shù)負(fù)責(zé)將所傳入之小時及分鐘數(shù)轉(zhuǎn)換成
    以分鐘為單位.  使用者自定函數(shù)時, 還有許多細(xì)節(jié)須留心, 如
    data scope,..
    ( 請參考 第十節(jié) Recursive Program)

    AWK中亦提供與 C 語言中相同的 Conditional Operator. 上式
    printf()中使用arrival >480 ? "*" : " "}即為一例
    若 arrival 大于 480 則return "*" , 否則return " ".
    % 為AWK之運(yùn)算子(operator), 其作用與 C 語言中之 % 相同
    (取余數(shù)).
    NR(Number of  Record) 為AWK的內(nèi)建變數(shù). 表AWK執(zhí)行該程序
    后所讀入的紀(jì)錄筆數(shù).
      AWK 中提供的 close( )指令, 語法如下(有二種) :

    close( filename )
    close( 置于pipe之前的command )
    為何本程序使用了兩個 close( ) 指令 :

    指令 close( "sort +2n >> today_rpt3" ), 其意思為 close 程序中
    置于 "sort +2n >> today_rpt3 " 之前的 Pipe, 并立刻呼叫 Shell 來
    執(zhí)行"sort +2n  >> today_rpt3".
    (若未執(zhí)行這指令, AWK必須于結(jié)束該程序時才會進(jìn)行上述動作;
    則這12筆sort后的數(shù)據(jù)將被 append 到檔案 today_rpt3 中
    "Average arrival time : ..." 的后方)
    因為 Shell 排序后的數(shù)據(jù)也要寫到 today_rpt3, 所以AWK必須
    先關(guān)閉使用中的today_rpt3 以利 Shell 正確將排序后的數(shù)據(jù)
    append 到today_rpt3否則2個不同的 process 同時開啟一
    檔案進(jìn)行輸出將會產(chǎn)生不可預(yù)期的結(jié)果.

    讀者應(yīng)留心上述兩點(diǎn),才可正確控制數(shù)據(jù)輸出到檔案中的順序.

    指令 close("sort +0n >> today_rpt3"中字符串 "sort +0n >> today_rpt3"
    須與 pipe | 后方的 Shell Command 名稱一字不差, 否則AWK將視為
    二個不同的 pipe.
    讀者可于BEGIN{}中先令變數(shù) Sys_call = "sort +0n  >> today_rpt3",
    程序中再一律以 Sys_call 代替該字符串.

    使用 getline 來讀取數(shù)據(jù)

    范 例 : 承上題,從檔案中讀取當(dāng)月遲到次數(shù), 并根據(jù)當(dāng)日出勤狀況
    更新遲到累計數(shù).(按不同的月份累計于不同的檔案)

    分 析:

       程序中自動抓取系統(tǒng)日期的月份名稱, 連接上``late.dat'',
    形成累計遲到次數(shù)的文件名稱(如 : Jullate.dat,...), 并以變數(shù)
    late_file紀(jì)錄該文件名.

    累計遲到次數(shù)的檔案中的數(shù)據(jù)格式為 :
                             員工代號(ID) 遲到次數(shù)
          例如, 執(zhí)行本程序前檔案 Auglate.dat 的內(nèi)容為 :

                     1012 0
                     1006 1
                     1052 2
                     1034 0
                     1005 0
                     1029 2
                     1042 0
                     1051 0
                     1008 0
                     1101 0
                     1025 1
                     1028 0   



    編寫程序 reformat4.awk 如下:
       awk '
    BEGIN {
               Sys_Sort = "sort +0n >> today_rpt4"
               Result = "today_rpt4"
               # 改變字段切割的方式
              # 令 Shell執(zhí)行``date''; getline 讀取結(jié)果,并以$0紀(jì)錄               
               FS = "[\t:]+"  
              "date" |  getline      
              print " Today is " , $2, $3 >Result
              print "=========================" > Result
              print `` ID  Number Arrival Time'' > Result
              close( Result )
    # 從文件按中讀取遲到數(shù)據(jù), 并用數(shù)組cnt[ ]記錄. 數(shù)組cnt[ ]中以
    員工代號為# 注標(biāo), 所對應(yīng)的值為該員工之遲到次數(shù).
    late_file = $2 "late.dat"            
    while( getline  < late_file >0 )  cnt[$1] = $2
    close( late_file )
    }
    {  
      # 已更改字段切割方式, $2表小時數(shù),$3表分鐘數(shù)
      arrival = HM_to_M($2, $3)  
      if( arrival > 480 ){
      mark = "*"  # 若當(dāng)天遲到,應(yīng)再增加其遲到次數(shù), 且令
      mark 為''*''.cnt[$1]++ }   
      else   mark = " "
               
    # message 用以顯示該員工的遲到累計數(shù), 若未曾遲到
    message 為空字符串
    message = cnt[$1] ? cnt[$1] " times" : ""
       printf("%s%2d:%2d %5s %s\n", $1, $2, $3, mark,
    message ) | Sys_Sort
             total += arrival
        }
    END  {
               close( Result )
               close( Sys_Sort )
               printf(" Average arrival time : %d:%d\n", total/NR/60,
           (total/NR)%60 ) >> Result
               #將數(shù)組cnt[ ]中新的遲到數(shù)據(jù)寫回檔案中
               for( any in cnt )
                     print any, cnt[any] > late_file
              }
       function HM_to_M( hour, min ){
                     return hour*60 + min
               }
      ' $*
    執(zhí)行后, today_rpt4 之內(nèi)容如下 :

              Today is  Aug 17
        ================================
          ID Number  Arrival Time
             1005        8:12       * 1 times
             1006        7:45         1 times
             1008        8: 1       * 1 times
             1012        7:46         
             1025        7:27         1 times
             1028        7:49         
             1029        7:57         2 times
             1034        7:26         
             1042        7:59         
             1051        7:51         
             1052        8: 5       * 3 times
             1101        7:32         
         Average arrival time : 7:49

    *
    說  明 :

    latefile 是一變量, 用以記錄遲到次數(shù)的檔案之檔名.
    latefile之值由兩部分構(gòu)成, 前半部是當(dāng)月月份名稱(由呼叫
    "date"取得)后半部固定為"late.dat" 如 : Junlate.dat.

    指令 getline <latefile 表由latefile所代表的檔案中
    讀取一筆紀(jì)錄, 并存放于$0.
    若使用者可自行把數(shù)據(jù)放入$0, AWK會自動對這新置入 $0 的數(shù)據(jù)
    進(jìn)行字段分割. 之后程序中可用$1, $2,..來表示該筆資料的第一欄,
    第二欄,..,
                  
    (注: 有少數(shù)AWK版本不容許使用者自行將數(shù)據(jù)置于 $0, 遇此情況可改
    用gawk或nawk)

    執(zhí)行g(shù)etline指令時, 若成功讀取紀(jì)錄,它會傳回1. 若遇到檔案結(jié)束,
    它傳回0; 無法開啟檔案則傳回-1.

    利用 while( getline  < filename >0 ) {....}可讀入檔案中
    的每一筆數(shù)據(jù)并予處理. 這是AWK中user自行讀取檔案數(shù)據(jù)的
    一個重要模式.

    數(shù)組 late_cnt[ ] 以員工ID. 當(dāng)注標(biāo)(index), 其對應(yīng)值表其遲到的
    次數(shù).

    執(zhí)行結(jié)束后, 利用 for(Variable in array ){..}之語法
    for( any in late_cnt ) print any, late_cnt[any]> latefile
    將更新過的遲到數(shù)據(jù)重新寫回記錄遲到次數(shù)之檔案. 該語法于第5節(jié)
    中曾有說明.

    處理 Multi-line 記錄
    AWK每次從數(shù)據(jù)文件中只讀取一筆Record, 進(jìn)行處理. AWK系依照其內(nèi)建變量 RS(Record Separator) 的定義將檔案中的數(shù)據(jù)分隔成一筆一筆的Record. RS 的默認(rèn)值是 "\n"(跳行符號), 故平常AWK中一行數(shù)據(jù)就是一筆 Record. 但有些檔案中一筆Record涵蓋了數(shù)行數(shù)據(jù), 這種情況下不能再以 "\n" 來分隔Records. 最常使用的方法是相鄰的Records之間改以 一個空白行 來隔開. 在AWK程序中, 令 RS = ""(空字符串)后, AWK把會空白行當(dāng)成來檔案中Record的分隔符. 顯然AWK對 RS = "" 另有解釋方式,簡略描述如下, 當(dāng) RS = "" 時 :
    數(shù)個并鄰的空白行, AWK僅視成一個單一的Record Saparator. (AWK不會于兩個緊并的空白行之間讀取一筆空的Record)
    AWK會略過(skip)檔首或檔末的空白行. 故不會因為檔首或檔末的空白行,造成AWK多讀入了二筆空的數(shù)據(jù).
    請觀察下例,首先建立一個數(shù)據(jù)文件 week.rpt如下: 張長弓 GNUPLOT 入門 吳國強(qiáng) Latex 簡介 VAST-2 使用手冊 mathematica 入門 李小華 AWK Tutorial Guide Regular Expression 該檔案檔首有數(shù)列空白行, 各筆Record之間使用一個或數(shù)個空白行隔開. 讀者請細(xì)心觀察, 當(dāng) RS = "" 時, AWK讀取該數(shù)據(jù)文件之方式. 編輯一個AWK程序檔案 make_report如下: awk ' BEGIN { FS = "\n" RS = "" split( "一. 二. 三. 四. 五. 六. 七. 八. 九.", C\_Number, " " ) } { printf("\n%s 報告人 : %s \n",C_Number[NR],$1) for( i=2; i { >}= NF; i++) printf(" %d. %s\n", i-1, $i) } ' $ 執(zhí)行 $ make_report week.rpt 屏幕產(chǎn)生結(jié)果如下: 一. 報告人 : 張長弓 1. GNUPLOT 入門 二. 報告人 : 吳國強(qiáng) 1. Latex 簡介 2. VAST-2 使用手冊 3. mathematica 入門 三. 報告人 : 李小華 1. AWK Tutorial Guide 2. Regular Expression 說明:
    本程序同時也改變字段分隔字符( FS= "\n", 如此一筆數(shù)據(jù)中的每一行都是一個field. 例如 : AWK讀入的第一筆 Record 為張長弓 GNUPLOT 入門 其中 $1 指的是"張長弓", $2 指的是"GNUPLOT 入門"
    上式中的C\_Number[ ]是一個數(shù)組(array), 用以記錄中文數(shù)字. 例如 : C\_Number[1] = "一", C\_Number[2] = "二" 這過程使用AWK字符串函數(shù) split( ) 來把中文數(shù)字放進(jìn)數(shù)組 Number[ ]中. 函數(shù) split( )用法如下 : split( 原字符串, 數(shù)組名, 分隔字符(field separator) ) : AWK將依所指定的分隔字符(field separator)分隔原字符串成一個個的字段(field), 并以指定的 數(shù)組 記錄各個被分隔的字段

    如何讀取命令列上的參數(shù)

    大部分的應(yīng)用程序都容許使用者于命令之后增加一些選擇性的參數(shù).
    執(zhí)行AWK時這些參數(shù)大部分用于指定數(shù)據(jù)文件文件名, 有時希望在程序
    中能從命令列上得到一些其它用途的數(shù)據(jù). 本小節(jié)中將敘述如何在
    AWK程序中取用這些參數(shù).
        建立檔案如下, 命名為 see_arg :
    {
            awk '
            BEGIN {
       for( i=0; i<ARGC ; i++)
            print ARGV  # 依次印出AWK所紀(jì)錄的參數(shù)
    }
    '$*
    執(zhí)行如下命令 :
    $  see_arg  first-arg  second-arg
    結(jié)果屏幕出現(xiàn) :
    awk
         first-arg
         second-arg

      說明 :

    ARGC, ARGV[ ] 為AWK所提供的內(nèi)建變量.
       
       ARGC : 為一整數(shù). 代表命令列上, 除了選項-v, -f 及其對應(yīng)
       的參數(shù)之外所有參數(shù)的數(shù)目.
       ARGV[ ] : 為一字符串?dāng)?shù)組. ARGV[0],ARGV[1],...ARGV[ARGC-1].
       分別代表命令列上相對應(yīng)的參數(shù).
       
       例如, 當(dāng)命令列為 :
       $awk  -vx=36  -f program1  data1  data2
        或
        awk '{ print $1 ,$2 }'  data1  data2
                其  ARGC    之值為  3
                ARGV[0] 之值為  "awk"
                ARGV[1] 之值為  "data1"
                ARGV[2] 之值為  "data2"

    命令列上的 "-f program1", " -vx=36", 或程序部分 '{ print $1, $2}'
    都不會列入 ARGC 及 ARGV[ ] 中.

    AWK 利用 ARGC 來判斷應(yīng)開啟的數(shù)據(jù)文件個數(shù).
    但使用者可強(qiáng)行改變 ARGC; 當(dāng) ARGC 之值被使用者設(shè)為 1 時;
    AWK將被蒙騙,誤以為命令列上并無數(shù)據(jù)文件文件名, 故不會以 ARGV[1],
    ARGV[2],..為文件名來開文件讀取數(shù)據(jù); 但于程序中仍可藉由 ARGV[1],
    ARGV[2],..來取得命令列上的資料.

    某一程序  test1.awk  如下 :
    BEGIN{
                number = ARGC   #先用number 記住實際的參數(shù)個數(shù).
                ARGC = 2 # 自行更改 ARGC=2, AWK將以為只有一個
                                   資料文件
                                # 仍可藉由ARGV[ ]取得命令列上的資料.
                 for( i=2; i< number; i++) data = ARGV
              }

         ........

           于命令列上鍵入
    $awk  -f test1.awk  data_file  apple  orange
    執(zhí)行時 AWK 會開啟數(shù)據(jù)文件 data_file 以進(jìn)行處理. 不會開啟以
    apple,orange 為檔名的檔案(因為 ARGC 被改成2). 但仍可藉由
    ARGV[2], ARGV[3]取得命令列上的參數(shù) apple, orange
       
      可以下列命令來達(dá)成上例的效果.

    $awk -f test2.awk -v data[2]="apple" -v data[3]="orange" data_file


    撰寫可與使用者相互交談的 AWK 程序

    執(zhí)行AWK程序時, AWK會自動由檔案中讀取數(shù)據(jù)來進(jìn)行
    處理, 直到檔案結(jié)束.只要將AWK讀取數(shù)據(jù)的來源改成鍵盤輸入,
    便可設(shè)計與AWK interactive talk 的程序.
    本節(jié)將提供一個該類程序的范例.
    [范例 :]  本節(jié)將撰寫一個英語生字測驗的程序, 它將印出中文字意,
    再由使用者回答其英語生字.
    首先編輯一個數(shù)據(jù)擋 test.dat (內(nèi)容不拘,格式如下)

             apple   蘋果
             orange  柳橙
             banana  香蕉
             pear    梨子
             starfruit 楊桃
             bellfruit 蓮霧
             kiwi     奇異果
             pineapple 菠蘿
             watermelon 西瓜


    編輯AWK程序"c2e"如下:

    awk '
    BEGIN {
               while( getline < ARGV[1] ){ #由指定的檔案中讀取測驗數(shù)據(jù)
               English[++n] = $1 # 最后, n 將表示題目之題數(shù)
               Chinese[n] = $2  
                }           
               ARGV[1] = "-"  # "-"表示由stdin(鍵盤輸入)
               srand()           # 以系統(tǒng)時間為隨機(jī)數(shù)啟始的種子
               question( )      #產(chǎn)生考題
                            
                }
      

        {# AWK自動讀入由鍵盤上輸入的數(shù)據(jù)(使用者回答的答案)
            if($1 != English[ind] )
                 print "Try again!"
            else{
            print "\nYou are right !! Press Enter to Continue --- "
            getline
            question( )#產(chǎn)生考題
        }  
      }

    function question(){
            ind = int(rand( )* n) + 1 #以隨機(jī)數(shù)選取考題
            system("clear"
            print " Press\"ctrl-d\" to exit"
            printf("\n%s ", Chinese[ind] " 的英文生字是: "
      }
      '$*

    執(zhí)行時鍵入如下指令 :
       $c2e  test.dat
      屏幕將產(chǎn)生如下的畫面:
      Press "ctrl-d " to exit
           蓮霧 的英文生字是:
      若輸入 bellfruit
    程序?qū)a(chǎn)生
        You are right !! Press Enter to Continue ---
    }
      說 明 :


    參數(shù) test.dat (ARGV[1]) 表示儲存考題的數(shù)據(jù)文件文件名.
    AWK 由該檔案上取得考題資料后, 將 ARGV[1] 改成 "-".
    "-" 表示由 stdin(鍵盤輸入) 數(shù)據(jù). 鍵盤輸入數(shù)據(jù)的結(jié)束符號 (End of file)
    是 Ctrl-d. 當(dāng) AWK 讀到 Ctrl-d 時就停止由 stdin 讀取數(shù)據(jù).
    AWK的數(shù)學(xué)函數(shù)中提供兩個與隨機(jī)數(shù)有關(guān)的函數(shù).
    rand( ) : 傳回介于 0與1之間的(近似)隨機(jī)數(shù)值. 0

    使用 AWK 撰寫 Recusive Program

    AWK 中除了函數(shù)的參數(shù)列(Argument List)上的參數(shù)(Arguments)外,
    所有變量不管于何處出現(xiàn)全被視為 Global variable. 其生命持續(xù)
    至程序結(jié)束 --- 該變量不論在function外或 function內(nèi)皆可使用,
    只要變量名稱相同所使用的就是同一個變量,直到程序結(jié)束.
    因 Recusive 函數(shù)內(nèi)部的變量, 會因它呼叫子函數(shù)(本身)而重復(fù)使用,
          故撰寫該類函數(shù)時, 應(yīng)特別留心.
    例如 : 執(zhí)行
    awk '
    BEGIN
    {
    x = 35
    y = 45
    test_variable( x )
    printf("Return to  main : arg1= %d, x= %d, y= %d, z= %d\n",
    arg1, x, y, z)
    }

      function test_variable( arg1 )
    {
    arg1++  # arg1 為參數(shù)列上的參數(shù), 是local variable. 離開此函數(shù)
    后將消失.
    y ++       # 會改變主式中的變量 y
    z = 55     # z 為該函數(shù)中新使用的變量, 主程序中變量 z 仍可被使用.
    printf("Inside the function: arg1=%d,x=%d, y=%d, z=%d\n",
         arg1, x, y, z)
    } '
    結(jié)果屏幕印出

             Inside the function: arg1= 36,x= 35, y= 46, z= 55
             Return to main     : arg1= 0, x= 35, y= 46, z= 55

      由上可知 :

    函數(shù)內(nèi)可任意使用主程序中的任何變量.
    函數(shù)內(nèi)所啟用的任何變量(除參數(shù)外), 于該函數(shù)之外依然可以使用.

    此特性優(yōu)劣參半, 最大的壞處是式中的變量不易被保護(hù), 特別是
    recursive呼叫本身, 執(zhí)行子函數(shù)時會破壞父函數(shù)內(nèi)的變量.
    權(quán)變的方法是 : 在函數(shù)的 Argument list 上虛列一些 Arguments.
    函數(shù)執(zhí)行中使用這些虛列的 Arguments 來記錄不想被破壞的數(shù)據(jù),
    如此執(zhí)行子函數(shù)時就不會破壞到這些數(shù)據(jù). 此外AWK 并不會檢查,
    呼叫函數(shù)時所傳遞的參數(shù)個數(shù)是否一致.
    例如 : 定義 recursive function 如下 :
              function  demo( arg1 )# 最常見的錯誤例子
                  ........
                   for(i=1; i< 20 ; i++){
                     demo(x)
      # 又呼叫本身. 因為 i 是 global variable, 故執(zhí)行完該子函數(shù)后
      # 原函數(shù)中的 i 已經(jīng)被壞, 故本函數(shù)無法正確執(zhí)行.
    .......
            }
    ..........     
      }
    可將上列函數(shù)中的 i 虛列在該函數(shù)的參數(shù)列上, 如此 i 便是一個
    local variable, 不會因執(zhí)行子函數(shù)而被破壞.
    將上列函數(shù)修改如下:
    function  demo( arg1,     i )
    {
    ......
    for(i=1; i< 20; i++)
         {
          demo(x)#AWK不會檢查呼叫函數(shù)時, 所傳遞的參數(shù)個數(shù)是否一致
          .....
          }
    }   
    $0, $1,.., NF, NR,..也都是 global variable, 讀者于 recusive function
    中若有使用這些內(nèi)建變量, 也應(yīng)另外設(shè)立一些 local variable 來保存,
    以免被破壞.
    范例 :以下是一個常見的 Recursive 范例. 它要求使用者輸入一串元素
    (各元素間用空白隔開) 然后印出這些元素所有可能的排列.
    編輯如下的AWK式, 取名為 permu
    awk '
    BEGIN
    {
    print "請輸入排列的元素,各元素間請用空白隔開"
    getline
    permutation($0, ""  
    printf("\n共 %d 種排列方式\n", counter)
    }
    function  permutation( main_lst, buffer, new_main_lst, nf, i, j )
    {
      $0 = main_lst  # 把main_lst指定給$0之后AWK將自動進(jìn)行
      字段分割.
    nf = NF            # 故可用 NF 表示 main_lst 上存在的元素個數(shù).
    #  BASE CASE : 當(dāng)main_lst只有一個元素時.
         if( nf == 1)
        {     
         print buffer main_lst # buffer的內(nèi)容連接(concate)上 main_lst 就
         counter++             # 是完成一次排列的結(jié)果
         return
        }
        # General Case : 每次從 main\_lst 中取出一個元素放到buffer中
        #  再用 main_lst 中剩下的元素 (new_main_lst) 往下進(jìn)行排列
          else for( i=1; i< =nf ;i++)
          {
           $0 = main_lst # $0($1,$2,..$j,,)為Global variable已被壞, 故重新
                               #  把 main\_lst 指定給\$0, 令A(yù)WK再做一次字段分割  
                     new_main_lst = ""
                     for(j=1; j< =nf; j++) # concate new_main_lst  
                        if( j != i )  new_main_lst = new_main_lst " " $j
                     permutation( new_main_lst, buffer " " $i  )
          }
      }
    '$*
    執(zhí)行     $ permu
      屏幕上出現(xiàn)

                  請輸入排列的元素,各元素間請用空白隔開
      若輸入 1 2 3 結(jié)果印出

                  1 2 3
                  1 3 2
                  2 1 3
                  2 3 1
                  3 1 2
                  3 2 1

                 共 6 種排列方式


    說 明 :

    有些較舊版的AWK,并不容許使用者指定$0之值. 此時可改用
    gawk, 或 nawk.
    否則也可自行使用 split() 函數(shù)來分割 main_lst.

    為避免執(zhí)行子函數(shù)時破壞 new_main_lst, nf, i, j 故把這些變數(shù)
    也列于參數(shù)列上. 如此, new_main_lst, nf, i, j 將被當(dāng)成 local variable,
    而不會受到子函數(shù)中同名的變量影響. 讀者宣告函數(shù)時,參數(shù)列上
    不妨將這些 ``虛列的參數(shù)'' 與真正用于傳遞信息的參數(shù)間以較長
    的空白隔開, 以便于區(qū)別.
    AWK 中欲將字符串concatenation(連接)時, 直接將兩字符串并置
    即可(Implicit Operator).
           例如 :

        awk '
       BEGIN{
              A = "This "
              B = "is a "
              C = A B "key." # 變量A與B之間應(yīng)留空白,
                                             否則''AB''將代表另一新變量.
                print C
                       }
               }
            結(jié)果將印出
            This is a key.

      AWK使用者所撰寫的函數(shù)可再reuse, 并不需要每個AWK式中
    都重新撰寫.
    將函數(shù)部分單讀編寫于一檔案中, 當(dāng)需要用到該函數(shù)時再以下列方式
    include進(jìn)來.
    $ awk  -f 函數(shù)襠名 -f AWK主程序文件名  數(shù)據(jù)文件文件名  


    Appendix A Patterns

    AWK 藉由判斷 Pattern 之值來決定是否執(zhí)行其后所對應(yīng)的
    Actions.這里列出幾種常見的 Pattern :
  • 論壇徽章:
    0
    2 [報告]
    發(fā)表于 2005-09-04 09:26 |只看該作者

    轉(zhuǎn)貼 awk Preface

    BEGIN  
    BEGIN 為 AWK 的保留字, 是一種特殊的 Pattern.
    BEGIN 成立(其值為true)的時機(jī)是 :
               ``AWK 程序一開始執(zhí)行, 尚未讀取任何數(shù)據(jù)之前.''
    所以在 BEGIN { Actions } 語法中, 其 Actions 部份僅于程序
    一開始執(zhí)行時被執(zhí)行一次. 當(dāng) AWK 從數(shù)據(jù)文件讀入數(shù)據(jù)列后,
    BEGIN 便不再成立, 故不論有多少數(shù)據(jù)列, 該 Actions 部份僅被執(zhí)行
    一次.
    一般常把 ``與數(shù)據(jù)文件內(nèi)容無關(guān)'' 與 ``只需執(zhí)行ㄧ次'' 的部分置于該
    Actions(以 BEGIN 為 Pattern)中.
    例如 :
    BEGIN {
            FS = "[ \t:]"   # 于程序一開始時, 改變AWK切割字段的方式
            RS = ""         # 于程序一開始時, 改變AWK分隔數(shù)據(jù)列的方式
            count = 100     # 設(shè)定變量 count 的起始值
            print " This is a title line "  # 印出一行 title
          }
           ....... # 其它 Pattern { Actions } .....

    有些AWK程序甚至''不需要讀入任何數(shù)據(jù)列''. 遇到這情況可把整個
    程序置于以 BEGIN 為 Pattern的 Actions 中.
    例如 :
                      BEGIN {   print " Hello ! the Word ! " }

    注意 :執(zhí)行該類僅含 BEGIN { Actions } 的程序時, AWK 并不會開啟
    任何數(shù)據(jù)文件進(jìn)行處理.

    END
    END 為 AWK 的保留字, 是另一種特殊的 Pattern.
    END 成立(其值為true)的時機(jī)與 BEGIN 恰好相反, 為 :
    ``AWK 處理完所有數(shù)據(jù), 即將離開程序時''
    平常讀入資料列時, END并不成立, 故其對應(yīng)的 Actions 并不被執(zhí)行;
    唯有當(dāng)AWK讀完所有數(shù)據(jù)時, 該 Actions 才會被執(zhí)行
    注意 : 不管數(shù)據(jù)列有多少筆, 該 Actions 僅被執(zhí)行一次.

    Relational Expression
    使用像 `` A  Relation Operator B'' 的 Expression 當(dāng)成 Pattern.  
    當(dāng) A 與 B 存在所指定的關(guān)系(Relation)時, 該 Pattern 就算成立(true).
            例如 :
               length($0)< = 80  { print }
            上式中 { length($0)<= 80 是一個 Pattern, 當(dāng) $0(數(shù)據(jù)列)之長度
            小于等于 80 時該 Pattern 之值為true, 將執(zhí)行其后的 Action
           (印出該資料列).   
            AWK 中提供下列 關(guān)系操作數(shù)(Relation Operator)


    操作數(shù)        涵意
    >        大于
    <        小于
    > =        大于或等于
    <=        小于或等于
    ==        等于
    !=        不等于
    ~        match
    !~        not match

    上列關(guān)系操作數(shù)除~(match)與!~(not match)外與 C 語言中之
    涵意一致.
    ~(match) 與!~(match) 在 AWK 之涵意簡述如下 :
    若 A 表一字符串, B 表一 Regular Expression.
    A ~B  判斷 字符串A 中是否 包含   能合于(match)B式樣的子字符串.
    A !~B 判斷 字符串A 中是否 未包含 能合于(match)B式樣的子字符串.
         例如 :
    $0 ~ /program[0-9]+\.c/  \{ print }
    $0 ~/program[0-9]+\.c/ }整個是一個 Pattern, 用來判斷$0(資料列)中
    是否含有可 match /program[0-9]+\.c/ 的子字符串, 若$0 中含有該類
    字符串, 則執(zhí)行 print (印出該列數(shù)據(jù)).
    Pattern 中被用來比對的字符串為$0 時(如本例), 可僅以 Regular Expression
    部分表之.
          故本例的 Pattern 部分
    $0 ~/program[0-9]+\.c/ 可僅用/program[0-9]+\.c/表之     

         (有關(guān) match 及 Regular Expression 請參考 附錄 E )
    Regular Expression
    直接使用 Regular Expression 當(dāng)成 Pattern; 此為 $0 ~
    Regular Expression 的簡寫.
    該 Pattern 用以判斷 $0(資料列) 中是否含有 match 該 Regular Expression
    的子字符串; 若含有該成立(true) 則執(zhí)行其對應(yīng)的 Actions.
         例如 :  
    /^[0-9]*$/  print "This line is a integer !"
    與{ $0 ~/^[0-9]*$/ { print "This line is a integer !" } 相同
    Compound Pattern
      之前所介紹的各種 Patterns, 其計算(evaluation)后結(jié)果為一邏輯值
    (True or False).AWK 中邏輯值彼此間可藉由&&(and),||(or), !(not)
    結(jié)合成一個新的邏輯值.故不同 Patterns 彼此可藉由上述結(jié)合符號
    來結(jié)合成一個新的 Pattern. 如此可進(jìn)行復(fù)雜的條件判斷.
    例 如 :
    FNR > = 23 &&   FNR <=28  print "    " $0      }
         上式利用&& (and) 將兩個 Pattern 求值的結(jié)果合并成一個邏輯值.
          該式 將資料文件中 第23行 到 28行 向右移5格(先印出5個空白
          字符)后印出.
          ( FNR 為AWK的內(nèi)建變量, 請參考 附錄 D )

    Pattern1 , Pattern2
    遇到這種 Pattern, AWK 會幫您設(shè)立一個 switch(或flag).
    當(dāng)AWK讀入的資料列使得 Pattern1 成立時, AWK 會打開(turn on)
    這 switch.
    當(dāng)AWK讀入的資料列使得 Pattern2 成立時, AWK 會關(guān)上(turn off)
    這個 switch.
    該 Pattern 成立的條件是 :
    當(dāng)這個 switch 被打開(turn on)時 (包括 Pattern1, 或 Pattern2 成立
    的情況)例 如 :
    FNR>= 23 && FNR< =28  { print "     " $0  }
         可改寫為
         FNR == 23 ,  FNR == 28           { print "      " $0 }
    說 明 :

    當(dāng) FNR >= 23 時, AWK 就 turn on 這個 switch;
    因為隨著資料列的讀入, AWK不停的累加 FNR.
    當(dāng) FNR = 28 時, Pattern2  FNR == 28 便成立, 這時 AWK
    會關(guān)上這個 switch.
    當(dāng) switch 打開的期間, AWK 會執(zhí)行 ``print "     " $0''

    ( FNR 為AWK的內(nèi)建變量, 請參考 附錄 D )


    Appendix B Actions

    Actions 是由下列指令(statement)所組成 :

      expression ( function calls, assignments..)
      print expression-list
      printf( format, expression-list)
      if( expression ) statement [else statement]
      while( expression ) statement
      do statement while( expression)
      for( expression; expression; expression) statement
      for( variable in array) statement
      delete
      break
      continue
      next
      exit [expression]
      statement

    AWK 中大部分指令與 C 語言中的用法一致, 此處僅介紹較為常用
    或容易混淆之指令的用法.

    流程控制指令

    if 指令
         語法
               if (expression) statement1 [else statement2 ]
           范例 :

      if( $1> 25 )
           print "The 1st field is larger than 25"
      else print "The 1st field is not larger than 25"

    (a)與 C 語言中相同, 若 expression 計算(evaluate)后之值不為 0
    或空字符串, 則執(zhí)行 statement1; 否則執(zhí)行 statement2.
    (b)進(jìn)行邏輯判斷的expression所傳回的值有兩種, 若最后的邏輯值
    為true, 則傳回1, 否則傳回0.
    (c)語法中else statement2 以[ ] 前后括住表示該部分可視需要而
    予加入或省略.

    while 指令
         語法 :
                 while( expression ) statement
         范例 :

                 while( match(buffer,/[0-9]+\.c/ ) ){
                 print "Find :" substr( buffer,RSTART, RLENGTH)
                 buff = substr( buffer, RSTART + RLENGTH)
            }

    上列范例找出 buffer 中所有能合于(match) /[0-9]+.c/(數(shù)字
    之后接上 ``.c''的所有子字符串).
    范例中 while 以函數(shù) match( )所傳回的值做為判斷條件. 若buffer
    中還含有合于指定條件的子字符串(match成功), 則 match()函數(shù)傳回1,
    while 將持續(xù)進(jìn)行其后之statement.
    do-while 指令
         語法 :
                  do statement while(expression)   
         范例 :

      do{
              print "Enter y or n ! "
              getline data <  "-"

         } while( data !~ /^[YyNn]$/)

    (a) 上例要求使用者從鍵盤上輸入一個字符, 若該字符不是
    Y, y, N, 或 n則會不停執(zhí)行該循環(huán), 直到讀取正確字符為止.
    (b)do-while 指令與 while 指令 最大的差異是 : do-while 指令會先執(zhí)行
    statement而后再判斷是否應(yīng)繼續(xù)執(zhí)行. 所以, 無論如何其 statement 部分
    至少會執(zhí)行一次.

    *
    for Statement 指令(一)
         語法 :
                 for(variable in  array ) statement
         范例 : 執(zhí)行下列命令

    awk '
          BEGIN{
                 X[1]= 50; X[2]= 60; X["last"]= 70
                 for( any in X )
                    printf("X[%d] = %d\n", any, X[any] )
               }'

    結(jié)果印出 :
                 X[2] = 60
                 X[last] = 70
                 X[1] = 50

    (a)這個 for 指令, 專用以搜尋陣中所有的index值, 并逐次使用所指定
    的變量予以紀(jì)錄. 以本例而言, 變量 any 將逐次代表 2, 1,及"last".
    (b)以這個 for 指令, 所搜尋出的index之值彼此間并無任何次續(xù)關(guān)系.
    (c)第7節(jié) Arrays in AWK 中有該指令的使用范例, 及解說.

    for Statement 指令(二)

         語法 :
                 for(expression1; expression2; expression3) statement  
    范例 :

        for(i=1; i< =10; i++)  sum = sum +  i

    說明 :
    (a)上列范例用以計算 1 加到 10 的總合.
    (b)expression1  常用于設(shè)定該 for 循環(huán)的起始條件, 如上例中的 i=1
        expression2 用于設(shè)定該循環(huán)的停止條件, 如上例中的 i<= 10
        expression3 常用于改變 counter 之值, 如上例中的 i++
    break 指令
      break 指令用以強(qiáng)迫中斷(跳離) for, while, do-while 等循環(huán).
      范例 :
    while(  getline < "datafile" > 0 )
    {
            if( $1 == 0 )      # 所讀取的數(shù)據(jù)置于 $0
                 break         # AWK立刻把 $0 上新的字段數(shù)據(jù)
            else               # 指定給 $1, $2, ...$NF
            print $2 / $1
    }

    上例中, AWK 不斷地從檔案 {datafile}中讀取數(shù)據(jù), 當(dāng)$1等于0時,
    就停止該執(zhí)行循環(huán).
    continue 指令
    循環(huán)中的 statement 進(jìn)行到一半時, 執(zhí)行 continue 指令來掠過回圈
    中尚未執(zhí)行的statement.
         范例 :

           for( index in X_array)
               {
                 if( index !~ /[0-9]+/ )  continue
                 print "There is a digital index", index
               }
    上例中若 index 不為數(shù)字則執(zhí)行 continue, 故將掠過(不執(zhí)行)其后
    的指令.
    需留心 continue 與 break 的差異 : 執(zhí)行 continue 只是掠過其后
    未執(zhí)行的statement, 但并未跳離開該循環(huán).
    next 指令
    執(zhí)行 next 指令時, AWK 將掠過位于該指令(next)之后的所有指令
    (包括其后的所有Pattern { Actions }), 接著讀取下一筆數(shù)據(jù)列,
    繼續(xù)從第一個 Pattern {Actions}
    執(zhí)行起.
         范例 :

    /^[ \t]*$/  {  print "This is a blank line! Do nothing here !"
                   next
                }
    $2 != 0   { print $1, $1/$2 }

    上例中, 當(dāng) AWK 讀入的資料列為空白行時( match /\^{}[\]*$/ )
    除打印訊息外且執(zhí)行 next, 故 AWK 將掠過其后的指令, 繼續(xù)讀取
    下一筆數(shù)據(jù), 從頭(第一個 Pattern \{ Actions \})執(zhí)行起.

    *
    exit 指令

    執(zhí)行 exit 指令時, AWK將立刻跳離(停止執(zhí)行)該AWK程序.

    AWK 中的 I/O 指令

    printf 指令
    該指令與 C 語言中的用法相同, 可藉由該指令控制數(shù)據(jù)輸出時
    的格式.
    語法 :
             printf("format", item1, item2,.. )
      范 例 :

          id = "BE-2647";  ave = 89
         printf("ID# : %s   Ave Score : %d\n", id, ave)


    (a)結(jié)果印出 :
        ID# :BE-647  Ave Score : 89
    (b)format 部分系由 一般的字符串(String Constant) 及 格式控制字符
    (Formatcontrol letter, 其前會加上一個\%字符)所構(gòu)成. 以上式為例
    "ID# : " 及 "  Ave Score : " 為一般字符串. %s 及 %d 為格式控制字符.
    (c)印出時, 一般字符串將被原封不動地印出. 遇到格式控制字符時,
    則依序把 format后方之 item 轉(zhuǎn)換成所指定的格式后印出.
    (d)有關(guān)的細(xì)節(jié), 讀者可從介紹 C 語言的書籍上得到較完整的介紹.
    (e)print 及 printf 兩個指令, 其后可使用>或< > 將輸出到stdout
    的數(shù)據(jù) Redirct到其它檔案, 7.1 Redirect Output to Files 中有完整的
    范例說明.
    print 指令
    范 例 :

        id = "BE-267";  ave = 89
        print "ID# :", id, "Ave Score :"ave

    (a)結(jié)果印出 :
        ID# : BE-267  Ave Score :89   
    (b)print 之后可接上字符串常數(shù)(Constant String)或變量. 它們彼此間
    可用``,'' 隔開.
    (c)上式中, 字符串 "ID# :" 與變量 id 之間使用``,''隔開, 印出時兩者之間
    會以自動 OFS(請參考 D 內(nèi)建變量 OFS) 隔開. OFS 之值一般內(nèi)定為
    "一個空格符"
    (d)上式中字符串 "Ave Score :" 與變量ave之間并未以``,''隔開, AWK
    會將這兩者先當(dāng)成字符串concate在一起(變成``Ave Score :89"后,
    再予印出
    (e)print 及 printf 兩個指令, 其后可使用> 或> 將輸出到stdout的
    數(shù)據(jù) Redirct到其它檔案, 7.1 Redirect Output to Files 中有完整的
    范例說明.
    getline 指令

    語法        由何處讀取數(shù)據(jù)        數(shù)據(jù)讀入后置于
    getline var> file        所指定的 file        變量 var(var省略時,表示置于$0)
    | getline var        pipe        變量 var(var省略時,表示置于$0)
    getline var        見 注一        變量 var(var省略時,表示置于$0)

    注一 : 當(dāng) Pattern 為 BEGIN 或 END 時, getline 將由 stdin 讀取數(shù)據(jù),
    否則由AWK正處理的數(shù)據(jù)文件上讀取數(shù)據(jù).
       
    getline 一次讀取一行數(shù)據(jù),
                  若讀取成功則return 1,
                  若讀取失敗則return -1,
                  若遇到檔案結(jié)束(EOF), 則return 0
    close  指令
    該指令用以關(guān)閉一個開啟的檔案, 或 pipe(見下例)
    范 例 :

    awk '
    BEGIN {  print "ID #   Salary" > "data.rpt" }   

          {  print $1 , $2 * $3  | "sort +0n > data.rpt" }   

    END{  close( "data.rpt" )
             close( "sort +0n > data.rpt" )
             print " There are", NR, "records processed."
            }

    說 明 :

    (a)上例中, 一開始執(zhí)行 print "ID #   Salary" > "data.rpt" 指令來
    印出一行抬頭. 它使用 I/O Redirection( > )將數(shù)據(jù)轉(zhuǎn)輸出到data.rpt,
    故此時檔案 {data.rpt} 是處于 Open 狀態(tài).
    (b)指令 print $1, $2 * $3 不停的將印出的資料送往 pipe(|), AWK
    于程序?qū)⒔Y(jié)束時才會呼叫 shell 使用指令 "sort +0n > data.rpt"
    來處理 pipe 中的數(shù)據(jù); 并未立即執(zhí)行, 這點(diǎn)與 Unix 中pipe的用法
    不盡相同.

    (c)最后希望于檔案 {data.rpt}之``末尾''處加上一行 "There are.....".
    但此時, Shell尚未執(zhí)行 "sort +0n > data.rpt" 故各數(shù)據(jù)列排序后
    的 ID 及 Salary 等數(shù)據(jù)尚未寫入data.rpt. 所以得命令 AWK
    提前先通知 Shell 執(zhí)行命令 "sort +0n > data.rpt" 來處理 pipe
    中的資料. AWK中這個動作稱為 close pipe. 系由執(zhí)行 close
    ( "shell command" )來完成. 需留心 close( )指令中的 shell command
    需與``|''后方的 shell command 完全相同(一字不差), 較佳的方法是
    先以該字符串定義一個簡短的變量, 程序中再以此變量代替該
    shell command   
    (d)為什么要執(zhí)行 close("data.rpt" ?  因為 sort 完后的資料也將寫到
    data.rpt,
    而該檔案正為AWK所開啟使用(write)中, 故AWK程序中應(yīng)先關(guān)閉
    data.rpt. 以免造成因二個 processes 同時開啟一個檔案進(jìn)行
    輸出(write)所產(chǎn)生的錯誤.
    system 指令   
    該指令用以執(zhí)行 Shell上 的 command.
    范 例 :

        DataFile = "invent.rpt"
        system( "rm " DataFile )  

    說明 :

    (a)system("字符串"指令接受一個字符串當(dāng)成Shell的命令. 上例中, 使用
    一個字符串常數(shù)"rm " 連接(concate)一個變量 DataFile 形成要求 Shell
    執(zhí)行的命令.Shell 實際執(zhí)行的命令為 ``rm invent.rpt''.  


    *
    ``|'' pipe指令
    ``|'' 配合 AWK 輸出指令, 可把 output 到 stdout 的數(shù)據(jù)繼續(xù)轉(zhuǎn)送給
    Shell 上的令一命令%
    當(dāng)成input的數(shù)據(jù).
      ``|''  配合 AWK getline 指令, 可呼叫 Shell 執(zhí)行某一命令, 再以 AWK
    的 getline 指令將該命令的所產(chǎn)生的數(shù)據(jù)讀進(jìn) AWK 程序中.
    范 例 :

       { print $1, $2 * $3  | "sort +1n > result" }      
      
        "date" |  getline  Date_data

    讀者請參考 7.2 Using System Resources 其中有完整的范例說明.        

    AWK 釋放所占用的內(nèi)存的指令
    AWK 程序中常使用數(shù)組(Array)來記憶大量數(shù)據(jù). delete 指令便是用來
    釋放數(shù)組中的元素所所占用的記憶空間.
    范 例 :

        for( any in X_arr )  
                 delete X_arr[any]

    讀者請留心, delete 指令一次只能釋放數(shù)組中的一個``元素''.
    AWK 中的數(shù)學(xué)操作數(shù)(Arithmetic Operators)
        +(加), -(減), *(乘), /(除), %(求余數(shù)), ^(指數(shù)) 與 C 語言中用法
    相同

    AWK 中的 Assignment Operators
        =, +=, -=, *= , /=, %=, ^=
        x += 5 的意思為 x = x + 5, 其余類推.

    AWK 中的 Conditonal  Operator
    語 法 :
    判斷條件 ? value1 : value2
    若 判斷條件 成立(true) 則傳回 value1, 否則傳回 value2.

    AWK 中的邏輯操作數(shù)(Logical Operators)
    &&( and ), ||or, !(not)
      Extended Regular Expression 中使用 ``|'' 表示 or 請勿混淆.

    AWK 中的關(guān)系操作數(shù)(Relational Operators)
    >, >=, <, < =, ==, !=, ~, !~

    AWK 中其它的操作數(shù)
        +(正號), -(負(fù)號),  ++(Increment Operator), - -(Decrement Operator)
    AWK 中各操作數(shù)的運(yùn)算優(yōu)先級( Precedence )
    (按優(yōu)先高低排列)   

        $       (字段操作數(shù), 例如 : i=3; $i表示第3欄)
        ^       (指數(shù)運(yùn)算)
        + ,- ,! (正,負(fù)號,及邏輯上的 not)
        * ,/ ,% (乘,除,余數(shù))
        + ,-    (加,減)  
        >, >  =,< , < =, ==, != (大于,大于等于,...,等關(guān)系符號)
        ~, !~   (match, not match)
        &&      (邏輯上的 and)
        ||      (邏輯上的 or )
        ? :     (Conditional Operator)
        = , +=, -=,*=, /=, %=, ^= (一些指定Assignment操作數(shù))


    AWK 的內(nèi)建函數(shù) Built-in Functions

    (一). 字符串函數(shù)

    index( 原字符串, 找尋的子字符串 ):
    若原字符串中含有欲找尋的子字符串,則傳回該子字符串在原字符串中第一次
    出現(xiàn)的位置,若未曾出現(xiàn)該子字符串則傳回0.
    例如執(zhí)行 :
    $awk  'BEGIN{ print index("8-12-94","-" }'
    結(jié)果印出 2
       
    length( 字符串 ) : 傳回該字符串的長度.
    例如執(zhí)行 :  
    awk  'BEGIN { print length("John" '}
    結(jié)果印出  4
    match( 原字符串, 用以找尋比對的 Regular Expression :
    AWK會在原字符串中找尋合乎Regular Expression的子字符串. 若合乎
    條件的子字符串有多個, 則以原字符串中最左方的子字符串為準(zhǔn).
    AWK找到該字符串后會依此字符串為依據(jù)進(jìn)行下列動作:

    設(shè)定AWK內(nèi)建變量 RSTART, RLENGTH :
              RSTART &=  合條件之子字符串在原字符串中之位置.
                     &=  0 ; 若未找到合條件的子字符串.
              RLENGTH &= 合條件之子字符串長度.
                      &= -1 ; 若未找到合條件的子字符串.
    傳回 RSTART 之值.
    例如執(zhí)行 :
        awk ' BEGIN {
                     match( "banana", /(an)+/ )
                     print RSTART, RLENGTH
                   }
              '       
           執(zhí)行結(jié)果印出 2  4

    split( 原字符串, 數(shù)組名, 分隔字符(field separator):
    AWK將依所指定的分隔字符(field separator)來分隔原字符串成
    一個個的字段(field),并以指定的數(shù)組記錄各個被分隔的字段.

         例如 :
               ArgLst = "5P12p89"
                split( ArgLst, Arr, /[Pp]/)
         執(zhí)行后   Arr[1]=5,  Arr[2]=12,  Arr[3]=89
    sprintf( 打印之格式, 打印之?dāng)?shù)據(jù), 打印之?dāng)?shù)據(jù),...)
    該函數(shù)之用法與AWK或C的輸出函數(shù)printf()相同. 所不同的是
    sprintf()會將要求印出的結(jié)果當(dāng)成一個字符串傳回
    一般最常使用sprintf()來改變數(shù)據(jù)格式. 如: x 為一數(shù)值資料, 若欲
    將其變成一個含二位小數(shù)的數(shù)據(jù),可執(zhí)行如下指令 :
      x = 28
      x = sprintf("%.2f",x)}
    執(zhí)行后 x = "28.00"

    sub( 比對用的 Regular Expression}, 將替換的新字符串, 原字符串 )
    sub( )將原字符串中第一個(最左邊)合乎所指定的Regular Expression
    的子字符串改以新字符串取代.

    第二個參數(shù)"將替換的新字符串"中可用"&"來代表"合于條件的
    子字符串"承上例,執(zhí)行下列指令:
    A = "a6b12anan212.45an6a"
             sub( /(an)+[0-9]*/, "[&]", A)
              結(jié)果印出ab12[anan212].45an6a
    sub()不僅可執(zhí)行取代(replacement)的功用,當(dāng)?shù)诙䝼參數(shù)為
    空字符串(""時,sub()所執(zhí)行的是``去除指定字符串''的功用.
    藉由 sub()與 match()的搭配使用,可逐次取出原字符串中合乎
    指定條件的所有子字符串.
    例如執(zhí)行下列程序:
    awk '
             BEGIN {
                   data = "p12-P34 P56-p61"
                  while( match( data ,/[0-9]+/) >0) {
                     print substr(data,{ RSTART, RLENGTH })
                     sub(/[0-9]+/,""
                     }
                  }
              ' $*  }
          結(jié)果印出 :
                    12
                    34
                    56
                    61
      sub( )中第三個參數(shù)(原字符串)若未指定,則其默認(rèn)值為$0.
      可用 sub( /[9-0]+/,"digital" ) 表示 sub(/[0-9]+/,"digital",$0 )

    gsub( 比對用的 Regular Expression}, 將替換的新字符串, 原字符串)
    這個函數(shù)與 sub()一樣,同樣是進(jìn)行字符串取代的函數(shù). 唯一不同點(diǎn)是

         gsub()會取代所有合條件的子字符串.
         gsub()會傳回被取代的子字符串個數(shù).

       請參考 sub().
    substr( 字符串,起始位置 [,長度] ):
    傳回從起始位置起,指定長度之子字符串. 若未指定長度,則傳回起始
    位置到自串末尾之子字符串.
    執(zhí)行下例
           awk {' BEGIN{ print  substr( "User:Wei-Lin Liu", 6)}
         結(jié)果印出
            Wei-Lin Liu
    (二). 數(shù)學(xué)函數(shù)

    int(x) : 傳回x的整數(shù)部分(去掉小數(shù)).
                例如 :
                    int(7. 將傳回 7
                    int(-7. 將傳回 -7

    sqrt(x) : 傳回x的平方根.
                例如 :
                sqart(9) 將傳回 3
                若 x 為負(fù)數(shù),則執(zhí)行 sqrt(x)時將造成 Run Time Error

    exp(x) : 將傳回e的x次方.
                例如 :
                exp(1) 將傳回 2.71828
                
    log(x) : 將傳回x以e為底的對數(shù)值.
                例如 :
                     log(e) = 1
                若 x< 0 ,則執(zhí)行 sqrt(x)時將造成 Run Time Error.

    sin(x) : x 須以徑度量為單位,sin(x)將傳回x的sin函數(shù)值.
    cos(x) : x 須以徑度量為單位,cos(x)將傳回x的cos函數(shù)值
    atan2(y,x) : 傳回 y/x 的tan反函數(shù)之值,傳回值系以徑度量為單位.
    rand( ) : 傳回介于 0與1之間的(近似)隨機(jī)數(shù)值; 0 < rand()<1.
    除非使用者自行指定rand()函數(shù)起始的seed,否則每次執(zhí)行AWK
    程序時,  rand()函數(shù)都將使用同一個內(nèi)定的seed,來產(chǎn)生隨機(jī)數(shù).
    srand(x) : 指定以x為rand( )函數(shù)起始的seed.
    若省略了x,則AWK會以執(zhí)行時的日期與時間為rand()函數(shù)起始的seed.

    AWK 的內(nèi)建變數(shù) Built-in Variables

    因內(nèi)建變量的個數(shù)不多, 此處按其相關(guān)性分類說明, 并未按其字母
    順序排列.

    ARGC 表命令列上除了選項 -F, -v, -f 及其所對應(yīng)的參數(shù)之外
    的所有參數(shù)的個數(shù).若將``AWK程序''直接寫于命令列上,
    則 ARGC 亦不將該``程序部分''列入計算.
    ARGV 一個數(shù)組用以記錄命令列上的參數(shù).
      例 : 執(zhí)行下列命令
      $awk  -F\t  -v a=8 -f prg.awk  file1.dat file2.dat
      或
      $awk  -F\t  -v a=8 '{ print $1 * a }' file1.dat file2.dat
             }
      執(zhí)行上列任一程序后
                        ARGC    =  3
                        ARGV[0] = "awk"
                        ARGV[1] = "file1.dat"
                        ARGV[2] = "file2.dat"
       讀者請留心 : 當(dāng) ARGC = 3 時, 命令列上僅指定 2 個數(shù)據(jù)文件.
       注 :
            -F\t 表示以 tab 為字段分隔字符 FS(field seporator).
            -v a=8 是用以 initialize 程序中的變量 a.
    FILENAME 用以表示目前正在處理的數(shù)據(jù)文件文件名.
    *
    FS   字段分隔字符.
    $0   表示目前AWK所讀入的資料列.
    $1,$2..分別表示所讀入的資料列之第一欄, 第二欄,..
    (參考下列說明)
    當(dāng)AWK讀入一筆資料列  ``A123  8:15'' 時,會先以$0 記載.
    故 $0 = "A123  8:15"
    若程序中進(jìn)一步使用了 $1, $2.. 或 NF 等內(nèi)建變數(shù)時, AWK才會
    自動分割 $0.
    以便取得字段相關(guān)的數(shù)據(jù). 切割后各個字段的數(shù)據(jù)會分別以
    $1, $2, $3...予以記錄.
    AWK內(nèi)定(default)的 字段分隔字符(FS) 為 空格符(及tab).
    以本例而言, 讀者若未改變 FS, 則分割后 :
    第一欄($1)="A123",  第二欄($2)="8:15".
    使用者可用 Regexp 自行定義 FS. AWK每次需要分割數(shù)據(jù)列時,
    會參考目前FS之值.
       例如 :
    令 FS = "[ :]+" 表示任何由 空白" " 或 ":" 所組成的字符串都可當(dāng)成
    分隔字符, 則分割后 :   
    第一欄($1) = "A123", 第二欄($2) = "8", 第三欄($3) = "15"
    NR 表從 AWK 開始執(zhí)行該程序后所讀取的數(shù)據(jù)列數(shù).
    FNR 與 NR 功用類似. 不同的是AWK每開啟一個新的數(shù)據(jù)文件,
    FNR 便從 0 重新累計

    NF表目前的資料列所被切分的字段數(shù).
    AWK 每讀入一筆數(shù)據(jù)后, 于程序中可以 NF 來得知該筆資料包含
    的字段個數(shù).在下一筆數(shù)據(jù)被讀入之前, NF 并不會改變. 但使用者
    若自行使用$0來記錄數(shù)據(jù)  
    例如 : 使用 getline, 此時 NF 將代表新的 $0 上所記載之?dāng)?shù)據(jù)的
    字段個數(shù).
    OFS輸出時的字段分隔字符. 默認(rèn)值 " "(一個空白), 詳見下面說明.
    ORS輸出時數(shù)據(jù)列的分隔字符. 默認(rèn)值 "\n"(跳行), 見下面說明.
    OFMT數(shù)值數(shù)據(jù)的輸出格式. 默認(rèn)值 "%.6g"(若須要時最多印出6位小數(shù))
    當(dāng)使用 print 指令一次印出多項數(shù)據(jù)時,
    例如 : print $1, $2
    印出數(shù)據(jù)時, AWK會自動在 $1 與 $2 之間補(bǔ)上一個 OFS 之值
    (默認(rèn)值為 一個空白)
    每次使用 print 輸出(印)數(shù)據(jù)后, AWK會自動補(bǔ)上 ORS 之值.
    (默認(rèn)值為 跳行)
    使用 print 輸出(印)數(shù)值數(shù)據(jù)時, AWK將采用 OFMT 之值為
    輸出格式.
    例如 : print 2/3
         AWK 將會印出 0.666667  
    程序中可藉由改變這些變量之值, 來改變指令 print 的輸出格式.  

    RS( Record Separator) : AWK從數(shù)據(jù)文件上讀取數(shù)據(jù)時,
    將依 RS 之定義把資料切割成許多Records,而AWK一次僅讀入一個
    Record,以進(jìn)行處理.
    RS 的默認(rèn)值是 "\n". 所以一般 AWK一次僅讀入一行數(shù)據(jù).
    有時一個Record含括了幾列資料(Multi-line Record). 這情況下不能
    再以"\n"
    來分隔并鄰的Records, 可改用 空白行 來分隔.
    在AWK程序中,令 RS = "" 表示以 空白行 來分隔并鄰的Records.
    *
    RSTART與使用字符串函數(shù) match( )有關(guān)之變量,詳見下面說明.
    RLENGTH與使用字符串函數(shù)match( )有關(guān)之變量.
    當(dāng)使用者使用 match(...) 函數(shù)后, AWK會將 match(...) 執(zhí)行的結(jié)果以
    RSTART,RLENGTH 記錄之.
    請參考 附錄  C AWK的內(nèi)建函數(shù) match().
    SUBSEP(Subscript Separator) 數(shù)組中注標(biāo)的分隔字符,
    默認(rèn)值為"\034"實際上, AWK中的 數(shù)組 只接受 字符串 當(dāng)它的注標(biāo),
    如 : Arr["John"].
    但使用者在 AWK 中仍可使用 數(shù)字 當(dāng)數(shù)組的注標(biāo), 甚至可使用多維的
    數(shù)組(Multi-dimenisional Array)
    如 : Arr[2,79]
    事實上, AWK在接受 Arr[2,79] 之前, 就已先把其注標(biāo)轉(zhuǎn)換成字符串
    "2\03479", 之后便以 Arr["2\03479"] 代替 Arr[2,79].
    可參考下例 :
    awk 'BEGIN { Arr[2,79] = 78
                        print  Arr[2,79]
                        print  Arr[ 2 , 79 ]
                        print  Arr["2\03479"]
                        idx = 2 SUBSEP 79
                        print Arr[idx]
                     }
                 ' $*
             執(zhí)行結(jié)果印出:
              78
              78
              78
              78


    Regular Expression 簡介


    為什么要使用 Regular Expression
    UNIX 中提供了許多 指令 或 tools, 它們具有在檔案中 尋找(Search)
    字符串或置換(Replace)字符串 的功能. 像 grep, vi , sed, awk,...
    不論是找尋字符串或置換字符串, 都得先 ``告訴這些指令所要找尋
    (被置換)的字符串為何''.若未能預(yù)先明確知道所要找尋(被置換)的字符串
    為何, 只知該字符串存在的范圍或特征時,
    例如 :
            (一)找尋 ``T0.c'', ``T1.c'', ``T2.c''.... ``T9.c'' 當(dāng)中的任一字符串.
            (二)找尋至少存在一個 ``A''的任意字符串.
    這情況下, 如何告知執(zhí)行找尋字符串的指令所要找尋的字符串為何.
    例 (一) 中, 要找尋任一在 ``T'' 與 ``.c'' 之間存在一個阿拉伯?dāng)?shù)字
    的字符串;當(dāng)然您可以列舉的方式, 一一把所要找尋的字符串告訴執(zhí)行
    命令的指令.但例 (二) 中合于該條件的字符串有無限種可能, 勢必?zé)o法
    一一列舉.
    此時,便需要另一種字符串表示的方法(協(xié)議).
      什么是 Regular Expression
    Regular Expression(以下簡稱 (Regexp)是一種字符串表達(dá)的方式.
    可用以指稱具有某特征的所有字符串.
    注 : 為區(qū)別于一般字符串, 本附錄中代表 Regexp 的字符串之前皆加
    ``Regexp''.注 : AWK 程序中常以/..../括住 Regexp; 以區(qū)別于一般字符串.

    組成 Regular Expression 的元素
    普通字符 除了.  * [ ] + ? ( ) \  \^ $ 外之所有字符.
    由普通字符所組成的Regexp其意義與原字符串字面意義相同.
    例如 : Regexp ``the'' 與一般字符串的 ``the'' 代表相同的意義.

    . Metacharacter : 用以代表任意一字符.
    須留心 UNIX Shell 中使用 ``*''表示 Wildcard, 可用以代表任意長度
    的字符串.而 Regexp 中使用 ``.'' 來代表一個任意字符.(注意: 并非
    任意長度的字符串)Regexp 中 ``*'' 另有其它涵意, 并不代表任意
    長度的字符串.
    ^ 表示該字符串必須出現(xiàn)于行首.   
    $ 表示該字符串必須出現(xiàn)于行末.  
    例如 :
         Regexp /^The/ 用以表示所有出現(xiàn)于行首的字符串 "The".
         Regexp /The$/ 用以表示所有出現(xiàn)于行末字符串 "The".
    \ 將特殊字符還原成字面意義的字符(Escape character)
    Regexp 中特殊字符將被解釋成特定的意義. 若要表示特殊字符的字面
    (literal meaning)意義時,在特殊字符之前加上"\"即可.
           例如 :
              使用Regexp來表示字符串 ``a.out''時, 不可寫成 /a.out/.
              因為 ``.''是特殊字符, 表任一字符. 可合乎 Regexp / a.out/
              的字符串將不只 ``a.out'' 一個; 字符串 ``a2out'', ``a3out'',      
              ``aaout'' ...都合于 Regexp /a.out/.
              正確的用法為 :  / a\.out/
    [...]字符集合, 用以表示兩中括號間所有的字符當(dāng)中的任一個.
    例如 : Regexp /[Tt]/ 可用以表示字符 ``T'' 或 ``t''.
          故 Regexp /[Tt]he/ 表示 字符串 ``The'' 或 ``the''.
          字符集合 [... ] 內(nèi)不可隨意留空白.
    例如 : Regexp /[ Tt ]/ 其中括號內(nèi)有空格符, 除表示
           ``T'', ``t'' 中任一個字符, 也可代表一個 `` ''(空格符)
    - 字符集合中可使用 ``-'' 來指定字符的區(qū)間, 其用法如下 :
    Regexp / [0-9]/ 等于 / [0123456789]/ 用以表示任意一個阿拉伯?dāng)?shù)字.
    同理 Regexp /[A-Z]/ 用以表示任意一個大寫英文字母.
    但應(yīng)留心 :

    Regexp /[0-9a-z]/ 并不等于 /[0-9][a-z]/; 前者表示一個字符,
          后者表示二個字符.
    Regexp /[-9]/ 或 /[9-]/ 只代表字符 ``9''或 ``-''.

    [^...]使用[^..] 產(chǎn)生字符集合的補(bǔ)集(complement set).
    其用法如下 :
    例如 : 要指定 ``T'' 或 ``t'' 之外的任一個字符, 可用 /[^Tt]/ 表之.
    同理 Regexp /[^a-zA-Z]/ 表示英文字母之外的任一個字符.
    須留心 ``^'' 之位置 : ``^''必須緊接于``["之后, 才代表字符集合的補(bǔ)集
    例如 :Regexp /[0-9\^]/ 只是用以表示一個阿拉伯?dāng)?shù)字或字符"^".
    *  形容字符重復(fù)次數(shù)的特殊字符.
    ``*'' 形容它前方之字符可出現(xiàn) 1 次或多次, 或不出現(xiàn).
    例如 :
    Regexp /T[0-9]*\.c 中 * 形容其前 [0-9] (一個阿拉伯?dāng)?shù)字)出現(xiàn)的次數(shù)
    可為 0次或 多次.故Regexp /T[0-9]*\.c/ 可用以表示
    ``T.c'', ``T0.c'', ``T1.c''...``T9.c''
    +形容其前的字符出現(xiàn)一次或一次以上.
    例如 : Regexp /[0-9]+/ 用以表示一位或一位以上的數(shù)字.
    ?  形容其前的字符可出現(xiàn)一次或不出現(xiàn).
    例如 : Regexp /[+-]?[0-9]+/ 表示數(shù)字(一位以上)之前可出現(xiàn)正負(fù)號
    或不出現(xiàn)正負(fù)號.
    (...)用以括住一群字符,且將之視成一個group(見下面說明)
            例如 :
           Regexp /12+/   表示字符串 ``12'', ``122'', ``1222'', ``12222'',...
           Regexp /(12)+/ 表示字符串 ``12'', ``1212'', ``121212'', ``12121212''....
    上式中 12 以( )括住, 故 ``+''所形容的是 12, 重復(fù)出現(xiàn)的也是 12.
    | 表示邏輯上的"或"(or)
    例如 :
    Regexp / Oranges? | apples?  | water/ 可用以表示 :
       字符串 ``Orange'', ``oranges'' 或 ``apple'', ``apples''  或 ``water''

    match是什么 ?  
    討論 Regexp 時, 經(jīng)常遇到 ``某字符串合于( match )某 Regexp''的字眼.
    其意思為 :  ``這個 Regexp 可被解釋成該字符串''.
    [ 例如] :
    字符串 "the" 合于(match) Regexp /[Tt]he/.
    因為 Regexp /[Tt]he/ 可解釋成字符串 "the" 或 "The", 故字符串 "the"
    或 "The"都合于(match) Regexp /[Th]he/.

    AWK 中提供二個關(guān)系操作數(shù)(Relational Operator,見注一) ~ !~,
    它們也稱之為 match, not match.但函義與一般常稱的 match 略有不同.
    其定義如下 :
    A  表一字符串, B 表一 Regular Expression
    只要 A 字符串中存在有子字符串可 match( 一般定義的 match) Regexp  B ,
    則 A ~B 就算成立, 其值為 true, 反之則為 false.
    ! ~ 的定義與~恰好相反.
    {itemize}
    例 如 :
              "another" 中含有子字符串 "the" 可 match Regexp /[Tt]he/ , 所以
              "another" ~/[Tt]he/  之值為 true.
    *
    [注 一] : 有些論著不把這兩個操作數(shù)( ~, !~)與 Relational Operators
    歸為一類.
                   
    應(yīng)用 Regular Expression 解題的簡例
    下面列出一些應(yīng)用 Regular Expression 的簡例, 部分范例中會更動
    $0 之值, 若您使用的 AWK
    不容許使用者更動 $0時, 請改用 gawk.   

    將檔案中所有的字符串 "Regular Expression" 或 "Regular expression"
    換成 "Regexp"
    awk '
       {   gsub( /Regular[ \t]+[Ee]xpression/, "Regexp"
           print
       }
      ' $*
    去除檔案中的空白行(或僅含空格符或tab)
    awk '
          $0 !~ /^[ \t]*$/  {  print   }
    ' $*
    在檔案中俱有 ddd-dddd(電話號碼型態(tài), d 表digital)的字符串前
    加上"TEL : "

    awk '
        {   gsub( /[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]/, "TEL : &" )
            print
        }
      ' $*

    從檔案的 Fullname 中分離出 路徑 與 文件名

    awk '
    BEGIN{
            Fullname = "/usr/local/bin/xdvi"
            match( Fullname, /.*\//)
            path = substr(Fullname, 1, RLENGTH-1)
            name = substr(Fullname, RLENGTH+1)
            print "path :", path,"  name :",name
          }
         ' $*
    結(jié)果印出
        path : /usr/local/bin   name : xdvi

    將某一數(shù)值改以現(xiàn)金表示法表之(整數(shù)部分每三位加一撇,
    且含二位小數(shù))

    awk '
    BEGIN {
             Number = 123456789
             Number = sprintf("$%.2f",Number)
             while( match(Number,/[0-9][0-9][0-9][0-9]/ ) )
                    sub(/[0-9][0-9][0-9][.,]/, ",&", Number)
             print Number
       
           }
       ' $*
    結(jié)果印出
        $123,456,789.00

    把檔案中所有具 ``program數(shù)字.f''形態(tài)的字符串改為
    ``[Ref : program數(shù)字.c]''

    awk '
        {   while( match( $0, /program[0-9]+\.f/ )  ){
               Replace = "[Ref : " substr( $0, RSTART, RLENGTH-2) ".c]"
               sub( /program[0-9]+\.f/, Replace)
             }
            print
         }
      ' $*     

    論壇徽章:
    0
    3 [報告]
    發(fā)表于 2005-09-04 09:48 |只看該作者

    轉(zhuǎn)貼 awk Preface

    我下在了這個的pdf

    論壇徽章:
    0
    4 [報告]
    發(fā)表于 2005-09-07 11:04 |只看該作者

    轉(zhuǎn)貼 awk Preface

    樓上的兄弟,能否給個pdf 的下載地租, roninlin@ek-chor-cn.com, 多謝了!!!

    論壇徽章:
    0
    5 [報告]
    發(fā)表于 2005-09-07 13:01 |只看該作者

    轉(zhuǎn)貼 awk Preface

    偶不喜歡PDF格式的,看起來不方便,不如純文本的.

    論壇徽章:
    0
    6 [報告]
    發(fā)表于 2005-09-07 15:09 |只看該作者

    轉(zhuǎn)貼 awk Preface

    我也想要PDF格式的,能否也給我一個!!!tzhjbest@yahoo.com.cn

    論壇徽章:
    0
    7 [報告]
    發(fā)表于 2005-09-07 15:50 |只看該作者

    轉(zhuǎn)貼 awk Preface

    樓上的2位兄弟收吧

    論壇徽章:
    0
    8 [報告]
    發(fā)表于 2005-09-10 09:42 |只看該作者

    轉(zhuǎn)貼 awk Preface

    dingyi129兄,給我也發(fā)一份吧
    joab_cn@126.com
    多謝。!

    論壇徽章:
    0
    9 [報告]
    發(fā)表于 2005-09-12 09:40 |只看該作者

    轉(zhuǎn)貼 awk Preface

    dingyi129兄,也給我一份吧,謝謝啦
    plwgong@yahoo.com.cn

    論壇徽章:
    0
    10 [報告]
    發(fā)表于 2005-09-12 10:47 |只看該作者

    轉(zhuǎn)貼 awk Preface

    發(fā)過去啦,收吧!
    您需要登錄后才可以回帖 登錄 | 注冊

    本版積分規(guī)則 發(fā)表回復(fù)

      

    北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年舉報專區(qū)
    中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
    感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP