玩轉 Shell 之:Shell 命令 Buffer 知多少?

來源:互聯網
上載者:User

標籤:style   http   color   io   os   ar   sp   檔案   資料   

1、問題:

下午有同學問了這麼一個問題:

tail -n +$(tail -n1 /root/tmp/n) -F /root/tmp/ip.txt 2>&1| awk ‘ARGIND==1{i=$0;next}{i++;if($0~/檔案已截斷/){i=0};print $1"---"i;print i >> "/root/tmp/n"}‘ /root/tmp/n -  seq 10 > /root/tmp/ip.txt && tail -f /root/tmp/n
把這兩條語句分別在同一台機器的兩個終端上執行,你會發現第二條語句的 tail 跟蹤不到結果,而第一條語句明明是有結果輸出的。

往下細說之前,咱們先簡單介紹下第一個語句幹嘛的:

這個語句是即時 tail 一份日誌,並實現了兩個小功能:

當檔案被重寫的時候將檔案的行號置 0,並且當進程掛掉後,重啟進程時,能從上次掛掉的地方開始 tail 起,類似“斷點續傳”。

不熟悉 awk 的同學看起來估計比較費勁,沒關係,咱們簡化下情境,寫個簡單的 test case 類比上面的語句(1):

{ echo 21;sleep 10;echo 22; }|awk ‘{print >> "/root/tmp/n"}‘
你會發現確實是當螢幕輸出了 21 的時候, n 的值沒有變化,但是當整個 echo 執行完成時,n 的值卻一起變化了,輸出了 21、22。

對此,你很容易寫出另一個 test case:

while [[ $i -lt 10 ]]; do ((i++)); echo $i|awk ‘{print >> "/root/tmp/n"}‘; sleep 2; done
那為什麼這個 case 能即時看到 n 的值在變化呢?別急,讀完本文,你自會找到答案。^ _ ^

其實語句(1)的問題在於 shell 下的一個概念引發的:buffer

寫過程式的同學應該知道 磁碟與記憶體,記憶體與CPU 的 IO 互動速度都不在一個量級上,那麼為了提高資料的存取效率,一般都會在軟體程式、硬體設計中採用 buffer 的設計,當 buffer 滿了才會請求一次 IO 操作,而不是一個字元或者一個位元組的方式請求 IO 操作,具體說來一般是互動會無 buffer 或者小 buffer,非互動操作一般 buffer 都會比較大,因為對使用者來說“即時性”要求不是那麼高了嘛~

語句(1) 的重新導向就是一個典型的非互動式操作,會由於 buffer 的原因,使用者無法即時的看到日誌中資料的變化。

2、解決方案:

知道原因了,咱們可以有如下幾種方式,讓 awk 中的重新導向變得即時輸出無 buffer:

{ echo 21;sleep 10;echo 22; }|awk ‘{print >> "/root/tmp/n";fflush("")}‘{ echo 21;sleep 10;echo 22; }|awk ‘{print >> "/root/tmp/n"; system("")}‘{ echo 21;sleep 10;echo 22; }|awk ‘{print >> "/root/tmp/n";close("/root/tmp/n")}‘{ echo 21;sleep 10;echo 22; }|awk ‘{system("echo "$0" >> /root/tmp/n")}‘{ echo 21;sleep 10;echo 22; }|awk ‘{print |"cat - >> /root/tmp/n"}‘
關於 fflush 的說明如下:
fflush([file]) Flush any buffers associated with the open output file or pipe file. If file is missing, then standard output is flushed. If file is the null string, then all open output files and pipes have their buffers flushed.
說道這兒,有同學或許會有疑問:還有什麼辦法去驗證是 buffer 的原因呢?

其實你調大你的輸出就行了:

{ seq 5000;sleep 10;seq 1000; }|awk ‘{print >> "/root/tmp/n"}‘
3、推而廣之

其實 linux shell 下的眾多命令都採用了 buffer 的設計,例如 grep,比如就曾經有同學問過我:

tail -f logfile | grep ‘ooxx‘ 為什麼看不到結果呢?日誌中明明就有的呀? 等等。。。

那本文在此稍稍總結下常用命令的 buffer 問題以及應對措施:

grep (e.g. GNU version 2.5.1)

--line-buffered

sed (e.g. GNU version 4.0.6)

-u,--unbuffered

awk (GNU awk)

use the fflush() function

awk (mawk)

-W interactive

tcpdump, tethereal

-l

例如上文提到的 grep buffer 問題:

tail -f /var/log/foo | grep --line-buffered

也有專門的命令或者工具包來解決這個問題,比如 stdbuf、unbuffer、stdbuf,或者直接調用 c 語言庫禁用 buffer:

setvbuf(stdout, 0, _IONBF, 0);
4、Refer:

[1] 9.1.4 Input/Output Functions

https://www.gnu.org/software/gawk/manual/html_node/I_002fO-Functions.html

[2] What is buffering? Or, why does my command line produce no output: tail -f logfile | grep ‘foo bar‘ | awk ...

http://mywiki.wooledge.org/BashFAQ/009

[3] How to fix stdio buffering

http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

[4] buffering in standard streams

http://www.pixelbeat.org/programming/stdio_buffering/

[5] 關於awk中通過管道執行shell後的管道關閉問題

http://hi.baidu.com/leejun_2005/item/26a5f8273e7e3555c28d5970

[6] Why does awk do full buffering when reading from a pipe

http://unix.stackexchange.com/questions/33650/why-does-awk-do-full-buffering-when-reading-from-a-pipe


玩轉 Shell 之:Shell 命令 Buffer 知多少?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.