GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Die Ausgabe der Prozesssubstitution ist nicht in Ordnung?

Die

echo one; echo two > >(cat); echo three; 

Befehl gibt unerwartete Ausgabe.

Ich habe das gelesen:Wie wird die Prozesssubstitution in Bash implementiert? und viele andere Artikel über Prozesssubstitution im Internet, verstehe aber nicht, warum es sich so verhält.

Erwartete Ausgabe:

one
two
three

Echter Output:

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

Auch diese beiden Befehle sollten aus meiner Sicht gleichwertig sein, tun es aber nicht:

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

Warum ich denke, sie sollten gleich sein? Denn beides verbindet die seq Ausgabe an cat Eingabe durch die anonyme Pipe – Wikipedia, Process Substitution.

Frage: Warum verhält es sich so? Wo ist mein Fehler? Die umfassende Antwort ist erwünscht (mit Erläuterung, wie die bash tut es unter der Haube).

Akzeptierte Antwort:

Ja, in bash wie in ksh (woher das Feature kommt), wird nicht auf die Prozesse innerhalb der Prozesssubstitution gewartet (bevor der nächste Befehl im Skript ausgeführt wird).

für einen <(...) Eins, das ist normalerweise in Ordnung wie in:

cmd1 <(cmd2)

die Shell wartet auf cmd1 und cmd1 wartet normalerweise auf cmd2 aufgrund dessen, dass bis zum Dateiende auf der ersetzten Pipe gelesen wird, und dieses Dateiende tritt normalerweise auf, wenn cmd2 stirbt. Das ist der gleiche Grund, warum mehrere Shells (nicht bash ) machen Sie sich nicht die Mühe, auf cmd2 zu warten in cmd2 | cmd1 .

Für cmd1>(cmd2) Dies ist jedoch im Allgemeinen nicht der Fall, da es sich eher um cmd2 handelt die typischerweise auf cmd1 wartet dort wird es im Allgemeinen danach beendet.

Das ist in zsh behoben die auf cmd2 wartet dort (aber nicht, wenn Sie es als cmd1>>(cmd2) schreiben und cmd1 nicht eingebaut ist, verwenden Sie {cmd1}>>(cmd2) stattdessen wie dokumentiert).

ksh wartet standardmäßig nicht, sondern lässt Sie mit wait darauf warten builtin (es macht auch die PID in $! verfügbar , obwohl das nicht hilft, wenn Sie cmd1>(cmd2)>(cmd3) ausführen )

rc (mit dem cmd1>{cmd2} Syntax), dasselbe wie ksh außer dass Sie die PIDs aller Hintergrundprozesse mit $apids erhalten können .

es (auch mit cmd1>{cmd2} ) wartet auf cmd2 wie in zsh , und wartet auch auf cmd2 in <{cmd2} Weiterleitungen verarbeiten.

bash macht die PID von cmd2 (oder genauer gesagt der Subshell, da sie cmd2 ausführt in einem untergeordneten Prozess dieser Subshell, obwohl es der letzte Befehl dort ist), der in $! verfügbar ist , lässt Sie aber nicht darauf warten.

Wenn Sie bash verwenden müssen , können Sie das Problem umgehen, indem Sie einen Befehl verwenden, der auf beide Befehle wartet, mit:

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

Das macht sowohl cmd1 und cmd2 haben ihre fd 3 offen zu einem Rohr. Katze wartet auf das Dateiende am anderen Ende, wird also normalerweise nur beendet, wenn beide cmd1 und cmd2 sind tot. Und die Shell wird auf diese cat warten Befehl. Sie könnten das als ein Netz sehen, um die Beendigung aller Hintergrundprozesse abzufangen (Sie können es für andere Dinge verwenden, die im Hintergrund gestartet werden, wie mit & , Coprocs oder sogar Befehle, die sich selbst im Hintergrund befinden, vorausgesetzt, sie schließen nicht alle ihre Dateideskriptoren, wie es normalerweise Daemons tun).

Verwandte Themen:Was führt dazu, dass Dateien Berechtigungen verlieren?

Beachten Sie, dass es dank des oben erwähnten verschwendeten Subshell-Prozesses auch dann funktioniert, wenn cmd2 schließt seine fd 3 (Befehle tun das normalerweise nicht, aber einige wie sudo oder ssh tun). Zukünftige Versionen von bash möglicherweise die Optimierung dort wie in anderen Shells durchführen. Dann bräuchten Sie etwas wie:

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

Um sicherzustellen, dass noch ein zusätzlicher Shell-Prozess mit diesem geöffneten fd 3 auf dieses sudo wartet Befehl.

Beachten Sie, dass cat wird nichts lesen (da die Prozesse nicht auf ihren fd 3 schreiben). Es dient lediglich der Synchronisation. Es wird nur ein read() ausgeführt Systemaufruf, der am Ende nichts zurückgibt.

Sie können tatsächlich vermeiden, cat auszuführen indem Sie eine Befehlssubstitution verwenden, um die Pipe-Synchronisierung durchzuführen:

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

Diesmal ist es die Shell statt cat das liest aus der Pipe, deren anderes Ende auf fd 3 von cmd1 offen ist und cmd2 . Wir verwenden eine Variablenzuweisung, also den Exit-Status von cmd1 ist in $? verfügbar .

Oder Sie könnten die Prozessersetzung von Hand durchführen und dann sogar das sh Ihres Systems verwenden da dies zur Standard-Shell-Syntax werden würde:

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

Beachten Sie jedoch, wie bereits erwähnt, dass nicht alle sh Implementierungen würden auf cmd1 warten nach cmd2 fertig ist (obwohl das besser ist als umgekehrt). Dieses Mal $? enthält den Exit-Status von cmd2; obwohl bash und zsh machen Sie cmd1 Der Exit-Status ist in ${PIPESTATUS[0]} verfügbar und $pipestatus[1][code> (siehe auch pipefail Option in einigen Shells, also $? kann den Ausfall anderer Rohrkomponenten als der letzten melden)

Beachten Sie, dass yash hat ähnliche Probleme mit seinem Prozess Umleitung Merkmal. cmd1>(cmd2) würde geschrieben werden cmd1 /dev/fd/3 3>(cmd2) dort. Aber cmd2 wird nicht gewartet und Sie können wait nicht verwenden auch darauf zu warten und seine PID wird nicht im $! zur Verfügung gestellt auch variabel. Sie würden die gleichen Problemumgehungen wie für bash verwenden .


Linux
  1. Neuer übergeordneter Prozess, wenn der übergeordnete Prozess stirbt?

  2. Ausgabe des „letzten“ Befehls?

  3. Wie ändere ich die Ausgabeumleitung eines laufenden Prozesses?

  4. Prozesssubstitution und Pipe?

  5. Wie kann ich die Ausgabe eines laufenden Prozesses in einer anderen Bash-Sitzung anzeigen?

Diff Wo Linien meistens gleich, aber außer Betrieb sind?

SIGTERM vs. SIGKILL:Was ist der Unterschied?

Prozesssubstitution:Eine ungewöhnliche, aber fortschrittliche Methode zur Eingabe-/Ausgabeumleitung in Linux

Führen Sie den Prozess mit Echtzeitausgabe in PHP aus

Linux:Finden Sie heraus, welcher Prozess den gesamten Arbeitsspeicher verwendet

Finden Sie heraus, ob das Betriebssystem in einer virtuellen Umgebung ausgeführt wird