Ubuntu Shell命令awk程序中使用
Ubuntu Shell 對于電腦使用的玩家的常用軟件,然后我就學習及深入的研究Ubuntu Shell ,在這里和大家一起探討Ubuntu Shell 的使用方法,希望對大家有用。
awk程序中使用 Ubuntu Shell 命令
awk程序中允許呼叫Ubuntu Shell指令. 并提供管道解決awk與系統間數據傳遞的問題. 所以awk很容易使用系統資源. 讀者可利用這個特點來編寫某些適用的系統工具.
寫一個awk程序來打印出線上人數. 將下列程序建文件, 命名為 count.awk BEGIN { while ( "who" | getline ) n++ print n } 并執行下列命令 : awk -f count.awk 執行結果將會印出目前在線人數
awk 程序并不一定要處理數據文件. 以本例而言, 僅輸入程序文件count.awk, 未輸入任何數據文件. BEGIN 和 END 同為awk中的一種 Pattern. 以 BEGIN 為 Pattern的Actions ,只有在awk開始執行程序,尚未開啟任何輸入文件前, 被執行一次.(注意: 只被執行一次)
"|" 為 awk 中表示管道的符號. awk 把 | 之前的字符串"who"當成Ubuntu Shell上的命令, 并將該命令送往Ubuntu Shell執行, 執行的結果(原先應于屏幕印出者)則藉由pipe送進awk程序中. getline為awk所提供的輸入指令.
其語法如下 : 語法由何處讀取數據數據讀入后置于getline var < file所指定的 file變量 var(var省略時,表示置于$0)getline varpipe 變量變量 var(var省略時,表示置于$0)
getline var見 注一 變量 var(var省略時,表示置于$0)
注一 : 當 Pattern 為 BEGIN 或 END 時, getline 將由 stdin 讀取數據, 否則由awk正處理的數據文件上讀取數據. getline 一次讀取一行數據, 若讀取成功則return 1, 若讀取失敗則return -1, 若遇到文件結束(EOF), 則return 0;
本程序使用 getline 所 return 的數據來做為 while 判斷循環停止的條件,某些awk版本較舊,并不容許使用者改變 $0 之值. 這種版的 awk 執行本程序時會產生 Error, 讀者可于 getline 之后置上一個變量 (如此, getline 讀進來的數據便不會被置于 $0 ), 或直接改用gawk便可解決.
awk 程序的應用實例
本節將示范一個統計上班到達時間及遲到次數的程序. 這程序每日被執行時將讀入二個文件: 員工當日到班時間的數據文件 ( 如下列之 arr.dat ) 存放員工當月遲到累計次數的文件. 當程序執行執完畢后將更新第二個文件的數據(遲到次數), 并打印當日的報表.這程序將分成下列數小節逐步完成, 其大綱如下:
在到班資料文件 arr.dat 之前增加一行抬頭 "ID Number Arrvial Time", 并產生報表輸出到文件today_rpt1 中.< 思考: 在awk中如何將數據輸出到文件 > 將 today_rpt1 上的數據按員工代號排序, 并加注執行當日日期; 產生文件 today_rpt2 <思考 awk中如何運用系統資源及awk中Pipe之特性 >
將awk程序包含在一個Ubuntu Shell script文件中于 today_rpt2 每日報表上, 遲到者之前加上"*", 并加注當日平均到班時間;產生文件 today_rpt3 從文件中讀取當月遲到次數, 并根據當日出勤狀況更新遲到累計數. <思考 使用者在awk中如何讀取文件數據 >
重定向輸出到文件
awk中并未提供如 C 語言中之fopen() 指令, 也未有fprintf() 文件輸出這樣的指令. 但awk中任何輸出函數之后皆可借助使用與UNIX 中類似的 I/O 重定向符, 將輸出的數據重定向到指定的文件; 其符號仍為 > (輸出到一個新產生的文件) 或 >> ( 添加輸出的數據到文件末尾 ).
[例 :]在到班數據文件 arr.dat 之前增加一行抬頭如下: "ID Number Arrival Time", 并產生報表輸出到文件 today_rpt1中 建立如下文件并取名為reformat1.awk BEGIN { print " ID Number Arrival Time" > "today_rpt1" print "===========================" > "today_rpt1" } { printf(" %s %s"n", $1,$2 ) > "today_rpt1" }
執行: $awk -f reformat1.awk arr.dat 執行后將產生文件 today_rpt1, 其內容如下 : ID Number Arrival Time awk程序中, 文件名稱 today_rpt1 的前后須以" (雙引號)括住, 表示 today_rpt1 為一字符串常量. 若未以"括住, 則 today_rpt1 將被awk解釋為一個變量名稱. 在awk中任何變量使用之前, 并不須事先聲明.
其初始值為空字符串(Null string) 或 0.因此程序中若未以 " 將 today_rpt1 括住, 則 today_rpt1 將是一變量, 其值將是空字符串, 這會在執行時造成錯誤(Unix 無法幫您開啟一個以空字符串為文件名的文件).
因此在編輯awk程序時, 須格外留心. 因為若敲錯變量名稱,awk在編譯程序時會認為是一新的變量, 并不會察覺. 因此往往會造成運行時錯誤. BEGIN 為awk的保留字, 是 Pattern 的一種. 以 BEGIN 為 Pattern 的 Actions 于awk程序剛被執行尚未讀取數據文件時被執行一次, 此后便不再被執行.
讀者或許覺得本程序中的I/O重定向符號應使用 " >>" (append)而非 " >". 本程序中若使用 ">" 將數據重導到 today_rpt1, awk 第一次執行該指令時會產生一個新檔 today_rpt1, 其后再執行該指令時則把數據追加到today_rpt1文件末, 并非每執行一次就重開一個新文件.
若采用">>"其差異僅在第一次執行該指令時, 若已存在today_rpt1則 awk 將直接把數據append在原文件之末尾. 這一點, 與UNIX中的用法不同.
awk 中如何利用系統資源
awk程序中很容易使用系統資源. 這包括在程序中途調用 Ubuntu Shell 命令來處理程序中的部分數據; 或在調用 Ubuntu Shell 命令后將其產生的結果交回 awk 程序(不需將結果暫存于某個文件). 這一過程是借助 awk 所提供的管道 (雖然有些類似 Unix 中的管道, 但特性有些不同),及一個從 awk 中呼叫 Unix 的 Ubuntu Shell 命令的語法來達成的.
承上題, 將數據按員工ID排序后再輸出到文件 today_rpt2 , 并于表頭附加執行時的日期. awk 提供與 UNIX 用法近似的 pipe, 其記號亦為 "|". 其用法及含意如下 : awk程序中可接受下列兩種語法:
[a. 語法] awk output 指令 | "Ubuntu Shell 接受的命令" ( 如 : print $1,$2 | "sort -k 1" ) [b. 語法] "Ubuntu Shell 接受的命令" | awk input 指令 ( 如 : "ls " | getline)
注 : awk input 指令只有 getline 一個. awk output 指令有 print, printf() 二個. 在a 語法中, awk所輸出的數據將轉送往 Ubuntu Shell , 由 Ubuntu Shell 的命令進行處理.以上例而言, print 所輸出的數據將經由 Ubuntu Shell 命令 "sort -k 1" 排序后再送往屏幕(stdout).
上列awk程序中, "print$1, $2" 可能反復執行很多次, 其輸出的結果將先暫存于 pipe 中,等到該程序結束時, 才會一并進行 "sort -k 1". 須注意二點 : 不論 print $1, $2 被執行幾次, "sort -k 1" 的執行時間是 "awk程序結束時",
"sort -k 1" 的執行次數是 "一次". 在 b 語法中, awk將先調用 Ubuntu Shell 命令. 其執行結果將通過 pipe 送入awk程序,以上例而言, awk先讓 Ubuntu Shell 執行 "ls",Ubuntu Shell 執行后將結果存于 pipe, awk指令 getline再從 pipe 中讀取數據.
使用本語法時應留心: 以上例而言,awk "立刻"調用 Ubuntu Shell 來執行 "ls", 執行次數是一次. getline 則可能執行多次(若pipe中存在多行數據). 除上列 a, b 二中語法外, awk程序中其它地方如出現像 "date", "cls", "ls"... 這樣的字符串, awk只把它當成一般字符串處理.
建立如下文件并取名為 reformat2.awk # 程序 reformat2.awk # 這程序用以練習awk中的pipe BEGIN { "date" | getline # Ubuntu Shell 執行 "date". getline 取得結果并以$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 -k 1 >>today_rpt2"}
執行如下命令: awk -f reformat2.awk arr.dat 執行后, 系統會自動將 sort 后的數據追加( Append; 因為使用 " >>") 到文件 today_rpt2末端. today_rpt2 內容如下 : awk程序由三個主要部分構成 :
[ i.] Pattern { Action} 指令 [ ii.] 函數主體. 例如 : function double( x ){ return 2*x } (參考第11節 Recursive Program ) [ iii.] Comment ( 以 # 開頭識別之 )
awk 的輸入指令 getline, 每次讀取一列數據. 若getline之后未接任何變量, 則所讀入之資料將以$0 記錄, 否則以所指定的變量儲存之.
執行 "date" | getline 后, $0 之值為 "2007年 09月 21日 星期五 14:28:02 CST",當 $0 之值被更新時, awk將自動更新相關的內建變量, 如: $1,$2,..,NF.故 $2 之值將為"09月", $3之值將為"21日".
(有少數舊版的awk不允許即使用者自行更新(update)$0的值,或者更新$0時,它不會自動更新 $1,$2,..NF. 這情況下, 可改用gawk或nawk. 否則使用者也可自行以awk字符串函數split()來分隔$0上的數據)
本程序中 printf() 指令會被執行12次( 因為有arr.dat中有12行數據), 但讀者不用擔心數據被重復sort了12次. 當awk結束該程序時才會 close 這個 pipe , 此時才將這12行數據一次送往系統,并呼叫 "sort -k 1 >> today_rpt2" 處理之.
awk提供另一個調用Ubuntu Shell命令的方法, 即使用awk函數system("Ubuntu Shell命令") 例如: $ awk ' BEGIN{ system("date > date.dat") getline < "date.dat"print "Today is ", $2, $3 }' 但使用 system( "Ubuntu Shell 命令" ) 時, awk無法直接將執行中的部分數據輸出給Ubuntu Shell 命令. 且 Ubuntu Shell 命令執行的結果也無法直接輸入到awk中.
【編輯推薦】