Wie verwende ich tee, um nach grep umzuleiten?

13

Ich habe nicht viel Erfahrung mit Tee, deshalb hoffe ich, dass dies nicht sehr einfach ist.

Nachdem ich eine der Antworten auf diese Frage gesehen hatte, stieß ich auf ein seltsames Verhalten mit tee.

Damit ich die erste Zeile und eine gefundene Zeile ausgeben kann, kann ich Folgendes verwenden:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Als ich dies zum ersten Mal ausführte (in zsh), war das Ergebnis in der falschen Reihenfolge. Die Spaltenüberschriften befanden sich unterhalb der grep-Ergebnisse (dies geschah jedoch nicht erneut). Daher habe ich versucht, die Befehle zu vertauschen:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Es wird nur die erste Zeile gedruckt, sonst nichts! Kann ich tee verwenden, um nach grep umzuleiten, oder mache ich das falsch?

Während ich diese Frage eintippte, funktionierte der zweite Befehl tatsächlich einmal für mich. Ich führte ihn fünfmal aus und kehrte dann zum Ergebnis einer Zeile zurück. Ist das nur mein System? (Ich lasse zsh in tmux laufen).

Schließlich, warum wird mit dem ersten Befehl "grep syslog" nicht als Ergebnis angezeigt (es gibt nur ein Ergebnis)?

Zur Kontrolle dient hier das grep ohne das tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

Update: Es scheint, dass head den gesamten Befehl abschneidet (wie in der Antwort unten angegeben). Der folgende Befehl gibt nun Folgendes zurück:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
Rqomey
quelle
Keine direkte Antwort auf Ihre Frage, aber es wäre viel sauberer, einfach so etwas zu tun ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013,
Ich habe noch nie an sed gedacht, ich denke, das ist vielleicht eine passende Antwort für die hier gestellte Frage, aber ich suche tatsächlich nach Informationen über das inkonsistente Verhalten dieser Befehle!
Rqomey

Antworten:

19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Die Befehle grepund headbeginnen ungefähr zur gleichen Zeit und beide empfangen die gleichen Eingabedaten nach Belieben, jedoch im Allgemeinen, sobald Daten verfügbar werden. Es gibt einige Dinge, die die 'unsynchronisierte' Ausgabe einführen können, die Zeilen umdreht. beispielsweise:

  1. Die gemultiplexten Daten von werden teetatsächlich zu einem Prozess vor dem anderen gesendet, was hauptsächlich von der Implementierung von abhängt tee. Eine einfache teeImplementierung wird eine readgewisse Menge an Input writeliefern und dann zweimal: Einmal zu stdout und einmal zu seinem Argument. Dies bedeutet, dass eines dieser Ziele zuerst die Daten abruft.

    Pipes sind jedoch alle gepuffert. Es ist wahrscheinlich, dass diese Puffer jeweils 1 Zeile umfassen, sie können jedoch auch größer sein, wodurch einer der empfangenen Befehle alles sehen kann, was er für die Ausgabe benötigt (dh die grepped-Zeile), bevor der andere Befehl ( head) Daten bei empfangen hat alle.

  2. Ungeachtet dessen ist es auch möglich, dass einer dieser Befehle die Daten empfängt, aber nicht in der Lage ist, rechtzeitig etwas damit zu tun, und der andere Befehl dann mehr Daten empfängt und sie schnell verarbeitet.

    Selbst wenn beispielsweise die Daten zeilenweise gesendet werden headund nicht wissen, wie sie zu behandeln sind (oder durch die Kernelplanung verzögert werden), können die Ergebnisse angezeigt werden, bevor sie überhaupt eine Chance bekommen. Fügen Sie zur Veranschaulichung eine Verzögerung hinzu: Dies gibt mit ziemlicher Sicherheit zuerst die Ausgabe aus.grepheadgrepheadps aux | tee >(sleep 1; head -n1) | grep sysloggrep

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Ich glaube, man bekommt hier oft nur eine Zeile, weil man headdie erste Eingabezeile erhält und dann die Standard- und Exit-Zeile schließt. Wenn teefestgestellt wird, dass das stdout geschlossen wurde, schließt es das eigene stdin (Ausgabe von ps) und wird beendet. Dies kann implementierungsabhängig sein.

Tatsächlich sind die einzigen Daten, die psgesendet werden können, die erste Zeile (definitiv, weil dies headkontrolliert wird) und möglicherweise einige andere Zeilen vor headund teeschließen ihre Standard-Deskriptoren.

Die Inkonsistenz, ob die zweite Zeile angezeigt wird, wird durch das Timing verursacht: headSchließt stdin, pssendet aber immer noch Daten. Diese beiden Ereignisse sind nicht gut synchronisiert, sodass die Zeile, die sie enthält, syslogimmer noch die Chance hat, teedas Argument (den grepBefehl) zu verwenden. Dies ähnelt den obigen Erläuterungen.

Sie können dieses Problem insgesamt vermeiden, indem Sie Befehle verwenden, die vor dem Schließen von stdin / exit auf alle Eingaben warten. Verwenden Sie beispielsweise awkanstelle von head, das alle seine Zeilen liest und verarbeitet (auch wenn sie keine Ausgabe verursachen):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Beachten Sie jedoch, dass die Linien wie oben immer noch nicht in der richtigen Reihenfolge angezeigt werden können. Dies kann wie folgt demonstriert werden:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

Ich hoffe, das war nicht zu detailliert, aber es gibt viele gleichzeitige Dinge, die miteinander interagieren. Separate Prozesse werden gleichzeitig ohne Synchronisierung ausgeführt, sodass die Aktionen bei bestimmten Ausführungen variieren können. Manchmal hilft es, tief in die zugrunde liegenden Prozesse einzudringen, um zu erklären, warum.

mrb
quelle
1
Hervorragende Antwort! Ich habe gefragt, weil ich an den zugrunde liegenden Prozessen interessiert bin. Wenn die Dinge unbeständig sind, finde ich es interessant. Wäre es besser zu rennen ps aux | tee >(grep syslog) | head -n1, wenn nicht mehr headstdout geschlossen würde ? Wow, dieser Befehl hat begonnen, jetzt eine Ausgabe zu geben, aber wie es in Übereinstimmung mit Ihrer Antwort passieren würde, scheint er abgeschnitten zu seinUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey
1
Sie können stattdessen etwas verwenden, das stdin nicht schließt head. Ich habe die Antwort mit diesem Beispiel aktualisiert:ps aux | tee >(grep syslog) | awk 'NR == 1'
Mrb
1
Wenn Sie @KrzysztofAdamski verwenden >(cmd), erstellt die Shell eine Named Pipe und übergibt diese als Argument an command ( tee). Dann teewird an stdout (weitergeleitet an awk) und auch an dieses Argument geschrieben. Es ist dasselbe wie mkfifo a_fifo ; grep ... a_fifoin einer Schale und ps | tee a_fifo | awk ...in einer anderen.
Mrb
1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/… - Try echo >(exit 0), das das von der Shell übergebene Argument wiedergibt (in meinem Fall wird es /dev/fd/63). Dies sollte auf bash und zsh genauso funktionieren.
Mrb
1
@mrb: es ist eine sehr interessante Funktion, die ich vorher nicht kannte, danke. Es funktioniert auf seltsame Weise in Bash, siehe jedoch pastebin.com/xFgRcJdF . Leider habe ich jetzt keine Zeit, dies zu untersuchen, aber ich werde es morgen tun.
Krzysztof Adamski
2

grep syslogwird nicht immer angezeigt, da dies vom Timing abhängt. Wenn Sie die Shell-Pipeline verwenden, führen Sie Befehle fast gleichzeitig aus. Aber der Schlüssel ist hier das Wort "fast". Wenn psdas Scannen aller Prozesse vor dem Start von grep abgeschlossen ist, wird es nicht in der Liste aufgeführt. Sie können zufällige Ergebnisse erhalten, abhängig von der Systemlast usw.

Ähnliches passiert mit deinem Tee. Es wird im Hintergrund in der Subshell ausgeführt und kann vor oder nach grep ausgelöst werden. Aus diesem Grund ist die Ausgabereihenfolge inkonsistent.

Bei der Tee-Frage ist das Verhalten ziemlich seltsam. Dies liegt daran, dass es nicht normal verwendet wird. Es wird ohne Argumente ausgeführt, was bedeutet, dass es nur Daten von der Standardeingabe in die Standardausgabe kopieren sollte. Die Standardausgabe wird jedoch an Subshell Running Head (im ersten Fall) oder Grep (im zweiten Fall) weitergeleitet. Es wird aber auch an den nächsten Befehl weitergeleitet. Ich denke, dass das, was in diesem Fall passiert, tatsächlich von der Implementierung abhängt. Zum Beispiel wird in meiner Bash 4.2.28 nie etwas in die Subshell stdin geschrieben. Auf zsh funktioniert es zuverlässig, wie Sie es möchten (Drucken der ersten Zeile von ps und der gesuchten Zeilen), jedes Mal, wenn ich es versuche,

Krzysztof Adamski
quelle
Das erklärt sowieso eine Sache, ich bin überrascht, dass Abschlagverzögerungen in spürbarem Umfang grep laufen!
Rqomey
0

Ein bisschen hackisch, aber hier ist meine Lösung in Form einer psgrep()Shell-Funktion, die ich benutze:

Leiten Sie die psKopfzeile um nach STDERRund dann nach grepon STDOUT, aber entfernen Sie zuerst den grepBefehl selbst, um zu vermeiden, dass die Zeile "noise" von grepselbst stammt:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
fnl
quelle