Mehrere Pipes in einem Tailed Log - die letzte Pipe empfängt nie die Standardeingabe

Mehrere Pipes in einem Tailed Log - die letzte Pipe empfängt nie die Standardeingabe

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 -fes verwendet selbst Zeilenpufferung; und das letzte grepschreibt auf das TTY, verwendet also auch Zeilenpufferung. Daher funktioniert Ihr erstes Beispiel.

Aber grepin 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 grepSystem 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 grepund man 1 unbuffer.man 1 stdbuf

Anmerkungen:

  • Keine der Lösungen ist portabel ( grep --line-bufferedund unbufferwird stdbufvon POSIX nicht spezifiziert).
  • Wenn Sie dies tun können, grep --line-bufferedsollten 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.
  • unbufferUndstdbuf arbeiten völlig unterschiedlich.
  • Bei ist stdbufdie 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 grepin eine weitere Datei geschrieben hat, verhält er sich wie der andere grep. Wenn Sie in diesem Fall möchten, dass Zeilen sofort in der endgültigen Datei erscheinen, sollten Sie auch das Verhalten des letzten Befehls ändern grep.
  • fishWenn in grepeine Wrapper-Funktion ist, erhalten Sie mit möglicherweise nicht das gewünschte Verhalten --line-buffered. Verwenden Sie command grep --line-buffered. Siehe diese Frage:Ausgabepipe wartet auf EOF infish.

Randbemerkung: tail foo.log | grep aaa | grep bbb(also tailohne -f) verursacht das Problem nicht, weil tailes beendet wird. Beim tailBeenden greperkennt der erste EOF, leert seinen Puffer und wird beendet, dann grepmacht der zweite dasselbe.

verwandte Informationen