awk指令碼學習小結

oh_maxy發表於2013-07-10
  awk是Linux/Unix系統中的shell過濾工具,它也是一種自解釋的程式語言。
  使用awk對某個檔案執行一些簡單操作,可以直接使用awk '' file 的命令格式操作檔案file。
  若是進行一些較複雜的理,語句會很長,不易維護。這時,我們可以使用awk指令碼來維護。

  awk指令碼是一個可執行的檔案,我們習慣以.awk來命名它。寫好的awk指令碼,加上可執行許可權來使用,格式:./do.awk file
  它的內部結構可以簡要概括為如下4個部分:
  $test.awk
  #! /bin/awk -f
  BEGIN{}
  {}
  END{}

  第一行告知指令碼系統中awk的位置。
  第二行是指令碼主體之前,執行些什麼。該行內的語句,可以只執行一次(這句話的意思,會在後文中舉例說明)。
  第三行是指令碼主體。此中的語句,會根據awk要處理的物件文字中的行數,以及匹配條件,執行零次或多次。
  第四行是指令碼主體之後,執行些什麼。(該部分與BEGIN類似,只是相對於主體的位置不同)。

  下面以處理grade.txt檔案(來自shell程式設計教材)為例,說明awk指令碼的使用方法(awk基本語法和函式,這裡不做說明)。
  grade.txt檔案內容為柔道學員比賽資訊(姓名 升段日期 學員編號 腰帶級別 年齡 目前積分 比賽最高分):
  M.Tansley  05/99   48311  Green   8   40   44
  J.Lulu     06/99   48317  Green   9   24   26
  P.Bunny    02/99   48     Yellow  12  35   28
  J.Troll    07/99   4842   Brown-3 12  26   26
  L.Tansley  05/99   4748   Brown-2 12  30   28
 
  現在,我們想列出學員姓名和目前積分。這個要求實現起來很容易,一條awk語句即可:awk '{print $1,$6}' grade.txt
  效果如下:
  M.Tansley 40
  J.Lulu 24
  P.Bunny 35
  J.Troll 26
  L.Tansley 30
 
  但是這個效果可能對於挑剔的我們來說,不盡人意:列與列對齊的很差,沒有列名,等等。
  好的,現在我們在原要求基礎上,再加三個要求:
  1. 要有列名Name和Score;
  2. 要在結尾顯示這幾個學員的總分和平均分;
  3. 要求列與列對齊。
  我們們先給出最終要求的效果,如下:
  Name       Score
  ================
  M.Tansley  40
  J.Lulu     24
  P.Bunny    35
  J.Troll    26
  L.Tansley  30
  The total is 155
  The average is 31

  這樣看起來是不是更美觀、合理?這樣的實現,雖然也可以一條awk語句搞定,但是那樣的語句很長,不利於維護。所以我們使用指令碼來維護。
  由於是初學,我的第一個awk指令碼是這樣寫的(stu.awk):
  #! /bin/awk -f
  {
  printf"%-10s %5s\n================\n","Name","Score";
  (tot+=$6);
  printf "%-10s %2d\n",$1,$6;
  print "The total is "tot;print "The average is "tot/NR
  }
  然而增加可執行許可權,執行./stu.awk grade.txt 後的效果卻是:
  Name       Score
  ================
  M.Tansley  40
  The total is 40
  The average is 40
  Name       Score
  ================
  J.Lulu     24
  The total is 64
  The average is 32
  Name       Score
  ================
  P.Bunny    35
  The total is 99
  The average is 33
  Name       Score
  ================
  J.Troll    26
  The total is 125
  The average is 31.25
  Name       Score
  ================
  L.Tansley  30
  The total is 155
  The average is 31

  這裡可見,主體部分的語句,會根據文字匹配行數,執行多次。於是又修改了一下,將Name和Score放到BEGIN中:
  我的第二個awk指令碼是這樣寫的(stu.awk):
  #! /bin/awk -f
  BEGIN{
  printf"%-10s %5s\n================\n","Name","Score"
  }
  {
  (tot+=$6);
  printf "%-10s %2d\n",$1,$6;
  print "The total is "tot;print "The average is "tot/NR
  }
 
  執行./stu.awk grade.txt的效果:
  Name       Score
  ================
  M.Tansley  40
  The total is 40
  The average is 40
  J.Lulu     24
  The total is 64
  The average is 32
  P.Bunny    35
  The total is 99
  The average is 33
  J.Troll    26
  The total is 125
  The average is 31.25
  L.Tansley  30
  The total is 155
  The average is 31

  這次大有改觀,Name和Score不會重複出現了。接下來就是總分和平均值重複的問題了。
  我的第三個awk指令碼是這樣寫的(stu.awk):
  #! /bin/awk -f
  BEGIN{
  printf"%-10s %5s\n================\n","Name","Score"
  }
  {
  (tot+=$6);
  printf "%-10s %2d\n",$1,$6;
  }
  END{
  print "The total is "tot;print "The average is "tot/NR
  }

  這次,終於出現了預期的結果:
  Name       Score
  ================
  M.Tansley  40
  J.Lulu     24
  P.Bunny    35
  J.Troll    26
  L.Tansley  30
  The total is 155
  The average is 31

  理論上的東西,我們也不多說了,相信通過這三個指令碼修改過程,我們應該可以感性的認識到awk指令碼中,BEGIN和END的作用了。希望這個例子,可以對初學awk指令碼的XDJM們,有所幫助。
  願大家工作愉快!
 
  PS:其實 ./stu.awk grade.txt等同於awk 'BEGIN{...}{...}END{...}' grade.txt(請將對應塊中的語句補齊即可,比較長,這也是用awk指令碼的原因)這條語句的效果,大家不妨試試看。

相關文章