1. 程式人生 > 其它 >shell管道咋堵住了

shell管道咋堵住了

原創:打碼日記(微信公眾號ID:codelogs),歡迎分享,轉載請保留出處。

背景

起因是這樣的,我們想開發一個小指令碼,當cpu使用率過高時,使用jstack將java的執行緒棧儲存下來,以便後面分析。

獲取cpu使用率

獲取cpu使用率是比較容易的,使用vmstat就可以了,第15列id就是cpu空閒率,用100減一下,就是cpu使用率了。

於是,我使用如下命令獲取了cpu使用率,發現能獲取到,如下:

vmstat 1|awk '{print 100-$15}'

問題出現

但當我在後面再加一個指令碼讀取cpu使用率時,卻發現當cpu到90%以上時,指令碼半天都沒有輸出,如下:

# 讓一個核滿載
stress --cpu 1

# cpu高時,自動jstack取執行緒棧
vmstat 1|awk '{print 100-$15}'|while read cpu; do [[ $cpu -gt 90 ]] && jstack `pgrep java`; done

我以為是我指令碼的問題,於是把後面的指令碼換成了cat,如下:

vmstat 1|awk '{print 100-$15}'|cat

發現還是沒有輸出,這就比較疑惑了,就好像最後那個管道被堵住了一樣!

問題解決

經過在網上一頓搜尋,終於發現答案,原來是快取的鍋。當awk的輸出目標是終端時,awk不會快取資料立馬輸出,而當輸出目標是檔案或管道時,awk會快取資料,到一定大小後再輸出。

並且,在awk中可以使用fflush函式,讓其立即輸出,如下:

vmstat 1|awk '{print 100-$15; fflush()}'|cat

同樣的,像grep, sed, python之類的命令,都有這樣的問題,可如下避免:

grep --line-buffered
sed -u
python -u

另外,Linux專門提供了一個stdbuf命令,用來避免命令輸出時快取資料,用法如下:

stdbuf -o L grep

最後,我的小指令碼修改如下,終於可以實現目標了。

vmstat 1|awk '{print 100-$15; fflush()}'|while read cpu; do [[ $cpu -gt 90 ]] && (jstack `pgrep java` > "$(date +'%FT%T')_stack.log"); done

往期內容

這grep咋還不支援\d呢(BRE,ERE,PCRE)


原來awk真是神器啊
Linux文字命令技巧(上)
Linux文字命令技巧(下)
字元編碼解惑