
Ich bin in einen interessanten Zustand unter Ubuntu geraten. Die folgenden Schritte beschreiben es am besten.
Mit einem einzelnen Rohr sehe ich, was ich erwarte
# In shell A
tail -f foo.log | grep aaa
# In shell B
echo aaa >> foo.log
# Shell A prints out `aaa`
Aber mit mehreren Rohren sehe ich überhaupt nichts
# 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
Aber es funktioniert einwandfrei, wenn ich nur ein Echo verwende.
echo 'aaa bbb' | grep aaa | grep bbb
Dies ist mein Versuch, eine minimale Reproduktion zu erstellen. Das Problem ist mir ursprünglich beim Versuch begegnet, Protokolle von adb logcat (Android-Entwicklertools) abzurufen. Ich habe es auch in zsh, bash und fish versucht.
Ich nahm an, dass es etwas mit meinem Inotify-Watcher-Limit zu tun hat, aber das Erhöhen hat nichts geändert.
Antwort1
Dies liegt an der Pufferung in der Pipe, die sich grundsätzlich nicht um Zeilen kümmert und Daten ansammeln kann.
Ich denke, tail -f
es verwendet selbst Zeilenpufferung; und das letzte grep
schreibt auf das TTY, verwendet also auch Zeilenpufferung. Daher funktioniert Ihr erstes Beispiel.
Aber grep
in der Mitte ist es anders und Sie müssen das Verhalten anpassen, indem Sie die Zeilenpufferung erzwingen oder deaktivieren. Die folgenden Befehle funktionieren wie erwartet.
Wenn Ihr
grep
System dies unterstützt--line-buffered
(unter Ubuntu ist dies der Fall):tail -f foo.log | grep --line-buffered aaa | grep bbb
Allgemeinere Lösungen (sie funktionieren mit vielen anderen Filtern als
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
Einzelheiten und Besonderheiten finden Sie unter man 1 grep
und man 1 unbuffer
.man 1 stdbuf
Anmerkungen:
- Keine der Lösungen ist portabel (
grep --line-buffered
undunbuffer
wirdstdbuf
von POSIX nicht spezifiziert). - Wenn Sie dies tun können,
grep --line-buffered
sollten Sie sich dafür entscheiden. Es macht keinen Sinn, zusätzliche Werkzeuge zu verwenden. - Verwandte Frage zu Unix und Linux SE:Pufferung in der Pipe deaktivieren.
unbuffer
Undstdbuf
arbeiten völlig unterschiedlich.- Bei ist
stdbuf
die Zeilenpufferung (-oL
) der Nichtpufferung ( ) vorzuziehen-o0
, da- es funktioniert höchstwahrscheinlich besser,
- und andere Teile Ihrer Pipe verwenden ohnehin die Zeilenpufferung.
- Wenn Ihr letzter Befehl
grep
in eine weitere Datei geschrieben hat, verhält er sich wie der anderegrep
. Wenn Sie in diesem Fall möchten, dass Zeilen sofort in der endgültigen Datei erscheinen, sollten Sie auch das Verhalten des letzten Befehls änderngrep
. fish
Wenn ingrep
eine Wrapper-Funktion ist, erhalten Sie mit möglicherweise nicht das gewünschte Verhalten--line-buffered
. Verwenden Siecommand grep --line-buffered
. Siehe diese Frage:Ausgabepipe wartet auf EOF infish
.
Randbemerkung: tail foo.log | grep aaa | grep bbb
(also tail
ohne -f
) verursacht das Problem nicht, weil tail
es beendet wird. Beim tail
Beenden grep
erkennt der erste EOF, leert seinen Puffer und wird beendet, dann grep
macht der zweite dasselbe.