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
ps aux | sed -n -e '1p' -e '/syslog/p'
.Antworten:
Die Befehle
grep
undhead
beginnen 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:Die gemultiplexten Daten von werden
tee
tatsächlich zu einem Prozess vor dem anderen gesendet, was hauptsächlich von der Implementierung von abhängttee
. Eine einfachetee
Implementierung wird eineread
gewisse Menge an Inputwrite
liefern 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
grep
ped-Zeile), bevor der andere Befehl (head
) Daten bei empfangen hat alle.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
head
und 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.grep
head
grep
head
ps aux | tee >(sleep 1; head -n1) | grep syslog
grep
Ich glaube, man bekommt hier oft nur eine Zeile, weil man
head
die erste Eingabezeile erhält und dann die Standard- und Exit-Zeile schließt. Wenntee
festgestellt wird, dass das stdout geschlossen wurde, schließt es das eigene stdin (Ausgabe vonps
) und wird beendet. Dies kann implementierungsabhängig sein.Tatsächlich sind die einzigen Daten, die
ps
gesendet werden können, die erste Zeile (definitiv, weil dieshead
kontrolliert wird) und möglicherweise einige andere Zeilen vorhead
undtee
schließen ihre Standard-Deskriptoren.Die Inkonsistenz, ob die zweite Zeile angezeigt wird, wird durch das Timing verursacht:
head
Schließt stdin,ps
sendet aber immer noch Daten. Diese beiden Ereignisse sind nicht gut synchronisiert, sodass die Zeile, die sie enthält,syslog
immer noch die Chance hat,tee
das Argument (dengrep
Befehl) 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
awk
anstelle vonhead
, das alle seine Zeilen liest und verarbeitet (auch wenn sie keine Ausgabe verursachen):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:
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.
quelle
ps aux | tee >(grep syslog) | head -n1
, wenn nicht mehrhead
stdout 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
head
. Ich habe die Antwort mit diesem Beispiel aktualisiert:ps aux | tee >(grep syslog) | awk 'NR == 1'
>(cmd)
, erstellt die Shell eine Named Pipe und übergibt diese als Argument an command (tee
). Danntee
wird an stdout (weitergeleitet anawk
) und auch an dieses Argument geschrieben. Es ist dasselbe wiemkfifo a_fifo ; grep ... a_fifo
in einer Schale undps | tee a_fifo | awk ...
in einer anderen.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.grep syslog
wird 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". Wennps
das 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,
quelle
Ein bisschen hackisch, aber hier ist meine Lösung in Form einer
psgrep()
Shell-Funktion, die ich benutze:Leiten Sie die
ps
Kopfzeile um nachSTDERR
und dann nachgrep
onSTDOUT
, aber entfernen Sie zuerst dengrep
Befehl selbst, um zu vermeiden, dass die Zeile "noise" vongrep
selbst stammt:quelle