下午有同窗問了這麼一個問題: html
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 跟蹤不到結果,而第一條語句明明是有結果輸出的。
在往下細說以前,我們先簡單介紹下第一個語句幹嗎的: node
這個語句是實時 tail 一份日誌,並實現了兩個小功能: linux
當文件被重寫的時候將文件的行號置 0,而且當進程掛掉後,重啓進程時,能從上次掛掉的地方開始 tail 起,相似「斷點續傳」。 shell
不熟悉 awk 的同窗看起來估計比較費勁,不要緊,我們簡化下場景,寫個簡單的 test case 模擬上面的語句(1): tcp
{ echo 21;sleep 10;echo 22; }|awk '{print >> "/root/tmp/n"}'你會發現確實是當屏幕輸出了 21 的時候, n 的值沒有變化,可是當整個 echo 執行完成時,n 的值卻一塊兒變化了,輸出了 2一、22。
對此,你很容易寫出另外一個 test case: 工具
while [[ $i -lt 10 ]]; do ((i++)); echo $i|awk '{print >> "/root/tmp/n"}'; sleep 2; done那爲何這個 case 能實時看到 n 的值在變化呢?別急,讀完本文,你自會找到答案。^ _ ^
其實語句(1)的問題在於 shell 下的一個概念引起的:buffer post
寫過程序的同窗應該知道 磁盤與內存,內存與CPU 的 IO 交互速度都不在一個量級上,那麼爲了提升數據的存取效率,通常都會在軟件程序、硬件設計中採用 buffer 的設計,當 buffer 滿了纔會請求一次 IO 操做,而不是一個字符或者一個字節的方式請求 IO 操做,具體說來通常是交互式的會無 buffer 或者小 buffer,非交互式的操做通常 buffer 都會比較大,由於對用戶來講「實時性」要求不是那麼高了嘛~ spa
語句 command1 | command2 大致的流程圖以下: 設計
例如以下語句的流程圖以下: unix
tail -f access.log | cut -d' ' -f1 | uniq
語句(1) 的重定向就是一個典型的非交互式操做,會因爲 buffer 的緣由,用戶沒法實時的看到日誌中數據的變化。
知道緣由了,我們能夠有以下幾種方式,讓 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"}'
其實 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
也有專門的命令或者工具包來解決這個問題,好比 unbuffer、stdbuf,或者直接調用 c 語言庫禁用 buffer:
setvbuf(stdout, 0, _IONBF, 0);
[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
[7] tailf and tail -f