
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
。次の質問を参照してください:出力パイプはEOFを待機しますfish
。
補足: tail foo.log | grep aaa | grep bbb
(つまり、tail
なし-f
) は終了するため、問題は発生しませんtail
。 がtail
終了すると、最初の がgrep
EOF を検出し、バッファーをフラッシュして終了し、2 番目の がgrep
同じことを行います。