shell管道咋堵住了
阿新 • • 發佈:2022-03-26
原創:打碼日記(微信公眾號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
往期內容
原來awk真是神器啊
Linux文字命令技巧(上)
Linux文字命令技巧(下)
字元編碼解惑