回到:linux
除了能夠從標準輸入或非選項型參數所指定的文件中讀取數據,還可使用getline從其它各類渠道獲取須要處理的數據,它的用法有不少種。shell
getline的返回值:dom
ERRNO
變量來描述錯誤爲了健壯性,getline時強烈建議進行判斷。例如:指針
上面的getline的括號儘可能加上,由於getline < 0
表示的是輸入重定向,而不是和數值0進行小於號的比較。code
getline無參數時,表示從當前正在處理的文件中當即讀取下一條記錄保存到$0
中,並進行字段分割,而後繼續執行後續代碼邏輯。對象
此時的getline會設置NF、RT、NR、FNR、$0和$N。blog
next也能夠讀取下一行。排序
getline:讀取下一行以後,繼續執行getline後面的代碼字符串
next:讀取下一行,當即回頭awk循環的頭部,不會再執行next後面的代碼get
它們之間的區別用僞代碼描述,相似於:
# next exec 9<> filename while read -u 9 line;do ...code... continue # next ...code... # 這部分代碼在本輪循環當中再也不執行 done # getline while read -u 9 line;do ...code... read -u 9 line # getline ...code... done
例如,匹配到某行以後,再讀一行就退出:
awk '/^1/{print;getline;print;exit}' a.txt
爲了更健壯,應當對getline的返回值進行判斷。
awk '/^1/{print;if((getline)<=0){exit};print}' a.txt
沒有參數的getline是讀取下一條記錄以後將記錄保存到$0
中,並對該記錄進行字段的分割。
一個參數的getline是將讀取的記錄保存到指定的變量當中,而且不會對其進行分割。
getline var
此時的getline只會設置RT、NR、FNR變量和指定的變量var。所以$0和$N以及NF保持不變。
awk ' /^1/{ if((getline var)<=0){exit} print var print $0"--"$2 }' a.txt
filename需使用雙引號包圍表示文件名字符串,不然會看成變量解析getline < "c.txt"
。此外,若是路徑是使用變量構建的,則應該使用括號包圍路徑部分。例如getline < dir "/" filename
中使用了兩個變量構建路徑,這會產生歧義,應當寫成getline <(dir "/" filename)
。
注意,每次從filename讀取以後都會作好位置偏移標記,下次再從該文件讀取時將根據這個位置標記繼續向後讀取。
例如,每次行首以1開頭時就讀取c.txt文件的全部行。
awk ' /^1/{ print; while((getline < "c.txt")>0){print}; close("c.txt") }' a.txt
上面的close("c.txt")
表示在while(getline)
讀取完文件以後關掉,以便後面再次讀取,若是不關掉,則文件偏移指針將一直在文件結尾處,使得下次讀取時直接遇到EOF。
cmd | getline
:從Shell命令cmd的輸出結果中讀取一條記錄保存到$0
中
$0 NF $N RT
,不會修改變量NR FNR
cmd | getline var
:從Shell命令cmd的輸出結果中讀取數據保存到var中
若是要再次執行cmd並讀取其輸出數據,則須要close關閉該命令。例如close("seq 1 5")
,參見下面的示例。
例如:每次遇到以1開頭的行都輸出seq命令產生的1 2 3 4 5
。
awk '/^1/{print;while(("seq 1 5"|getline)>0){print};close("seq 1 5")}' a.txt
再例如,調用Shell的date命令生成時間,而後保存到awk變量cur_date中:
awk ' /^1/{ print "date +\"%F %T\""|getline cur_date print cur_date close("date +\"%F %T\"") }' a.txt
能夠將cmd保存成一個字符串變量。
awk ' BEGIN{get_date="date +\"%F %T\""} /^1/{ print get_date | getline cur_date print cur_date close(get_date) }' a.txt
更爲複雜一點的,cmd中能夠包含Shell的其它特殊字符,例如管道、重定向符號等:
awk ' /^1/{ print if(("seq 1 5 | xargs -i echo x{}y 2>/dev/null"|getline) > 0){ print } close("seq 1 5 | xargs -i echo x{}y 2>/dev/null") }' a.txt
awk雖然強大,可是有些數據仍然不方便處理,這時可將數據交給Shell命令去幫助處理,而後再從Shell命令的執行結果中取回處理後的數據繼續awk處理。
awk經過|&
符號來支持coproc。
awk_print[f] "something" |& Shell_Cmd Shell_Cmd |& getline [var]
這表示awk經過print輸出的數據將傳遞給Shell的命令Shell_Cmd去執行,而後awk再從Shell_Cmd的執行結果中取回Shell_Cmd產生的數據。
例如,不想使用awk的substr()來取子串,而是使用sed命令來替換。
awk ' BEGIN{ CMD="sed -nr \"s/.*@(.*)$/\\1/p\""; } NR>1{ print $5; print $5 |& CMD; close(CMD,"to"); CMD |& getline email_domain; close(CMD); print email_domain; }' a.txt
對於awk_print |& cmd; cmd |& getline
的使用,須注意的是:
awk_print |& cmd
會直接將數據寫進管道,cmd能夠從中獲取數據close(cmd,"to")
,這樣表示向管道中寫入一個EOF標記,避免某些要求讀完全部數據再執行的cmd命令被永久阻塞stdbuf -oL cmd
以強制其按行緩衝輸出數據
CMD="stdbuf -oL cmdline";awk_print |& CMD;close(CMD,"to");CMD |& getline
對於那些要求讀完全部數據再執行的命令,例如sort命令,它們有可能須要等待數據已經完成後(遇到EOF標記)纔開始執行任務,對於這些命令,能夠屢次向coprocess中寫入數據,最後close(CMD,"to")
讓coprocess運行起來。
例如,對age字段(即$4
)使用sort命令按數值大小進行排序:
awk ' BEGIN{ CMD="sort -k4n"; } # 將全部行都寫進管道 NR>1{ print $0 |& CMD; } END{ close(CMD,"to"); # 關閉管道通知sort開始排序 while((CMD |& getline)>0){ print; } close(CMD); } ' a.txt
close(filename) close(cmd,[from | to]) # to參數只用於coprocess的第一個階段
若是close()關閉的對象不存在,awk不會報錯,僅僅只是讓其返回一個負數返回值。
close()有兩個基本做用:
awk中任何文件都只會在第一次使用時打開,以後都不會再從新打開。只有關閉以後,再使用纔會從新打開。
例如一個需求是隻要在a.txt中匹配到1開頭的行就輸出另外一個文件x.log的全部內容,那麼在第一次輸出x.log文件內容以後,文件偏移指針將在x.log文件的結尾處,若是不關閉該文件,則後續全部讀取x.log的文件操做都從結尾處繼續讀取,可是顯然老是獲得EOF異常,因此getline返回值爲0,並且也讀取不到任何數據。因此,必須關閉它才能在下次匹配成功時再次從頭讀取該文件。
awk ' /^1/{ print; while((getline var <"x.log")>0){ print var } close("x.log") }' a.txt
在處理Coprocess的時候,close()能夠指定第二個參數"from"或"to",它們都針對於coproc而言,from時表示關閉coproc |& getline
的管道,使用to時,表示關閉print something |& coproc
的管道。
awk ' BEGIN{ CMD="sed -nr \"s/.*@(.*)$/\\1/p\""; } NR>1{ print $5; print $5 |& CMD; close(CMD,"to"); # 本次close()是必須的 CMD |& getline email_domain; close(CMD); print email_domain; }' a.txt
上面的第一個close是必須的,不然sed會一直阻塞。由於sed一直認爲還有數據可讀,只有關閉管道發送一個EOF,sed纔會開始處理。