
我在 Ubuntu 上進入了有趣的狀態。下面的步驟對其進行了最好的描述。
透過一根管道,我看到了我所期望的
# In shell A
tail -f foo.log | grep aaa
# In shell B
echo aaa >> foo.log
# Shell A prints out `aaa`
但是對於多個管道我什麼也看不到
# In shell A
tail -f foo.log | grep aaa | grep bbb
# In shell B
echo aaa bbb >> foo.log
# Nothing ever prints in shell A
但如果我只是迴聲的話,效果很好。
echo 'aaa bbb' | grep aaa | grep bbb
這是我創建最小複製的嘗試——我最初在嘗試從 adb logcat(Android 開發工具)獲取日誌時遇到了問題。我也嘗試過 zsh、bash 和 Fish。
我認為這與我的 inotify 觀察者限制有關,但增加它並沒有改變任何東西。
答案1
這是因為管道中的緩衝,通常不關心行並且可以累積資料。
我認為tail -f
單獨使用行緩衝;最後一個grep
寫入 tty,因此它也使用行緩衝。因此你的第一個例子是有效的。
但grep
中間是不同的,您需要透過強制行緩衝或停用緩衝來調整其行為。以下命令將按您的預期工作。
如果您
grep
支持--line-buffered
(Ubuntu 中支持):tail -f foo.log | grep --line-buffered aaa | grep bbb
更通用的解決方案(它們將與除 之外的許多過濾器一起使用
grep
):tail -f foo.log | unbuffer -p grep aaa | grep bbb tail -f foo.log | stdbuf -oL grep aaa | grep bbb tail -f foo.log | stdbuf -o0 grep aaa | grep bbb
請參閱man 1 grep
、man 1 unbuffer
和man 1 stdbuf
了解詳細資訊和怪癖。
筆記:
- 這兩種解決方案都不是可移植的(
grep --line-buffered
,unbuffer
且stdbuf
POSIX 未指定)。 - 如果你能做到這一點,
grep --line-buffered
那麼它應該是你的選擇。使用額外的工具是沒有意義的。 - Unix & Linux SE 的相關問題:關閉管道中的緩衝。
unbuffer
和stdbuf
以完全不同的方式工作。- 對於
stdbuf
,行緩衝 (-oL
) 應該優先於無緩衝 (-o0
) ,因為- 它很可能表現得更好,
- 無論如何,管道的其他部分都使用行緩衝。
- 如果您最後一次
grep
寫入另一個文件,它的行為會像另一個grep
.在這種情況下,如果您希望行立即出現在最終文件中,那麼您也應該修改最後一個grep
. - 在 中
fish
,如果grep
是包裝函數,則您可能無法使用 獲得所需的行為--line-buffered
。使用command grep --line-buffered
。看這個問題:輸出管道等待 EOFfish
。
附註:(tail foo.log | grep aaa | grep bbb
即tail
沒有-f
)不會導致問題,因為tail
退出。退出時tail
,第一個grep
偵測到 EOF,刷新其緩衝區並退出,然後第二個grep
執行相同操作。