Ich wollte also eine Antwort wie die von Lesmana beisteuern, aber ich denke, meine ist vielleicht eine etwas einfachere und etwas vorteilhaftere reine Bourne-Shell-Lösung:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Ich denke, dies lässt sich am besten von innen nach außen erklären - Befehl1 wird ausgeführt und druckt seine reguläre Ausgabe auf stdout (Dateideskriptor 1). Sobald dies erledigt ist, wird printf den Exit-Code von icommand1 ausführen und auf seinem stdout drucken, aber dieser stdout wird umgeleitet Dateideskriptor 3.
Während Befehl1 ausgeführt wird, wird seine Standardausgabe an Befehl2 weitergeleitet (die Ausgabe von printf gelangt nie zu Befehl2, da wir sie an Dateideskriptor 3 anstatt an 1 senden, was die Pipe liest). Dann leiten wir die Ausgabe von command2 in den Dateideskriptor 4 um, so dass sie auch außerhalb des Dateideskriptors 1 bleibt - weil wir den Dateideskriptor 1 für ein wenig später frei haben möchten, weil wir die printf-Ausgabe auf Dateideskriptor 3 wieder in den Dateideskriptor zurückbringen 1 - weil dies die Befehlssubstitution (die Backticks) erfasst und in die Variable eingefügt wird.
Das letzte bisschen Magie ist, dass exec 4>&1
wir es zuerst als separaten Befehl getan haben - es öffnet den Dateideskriptor 4 als Kopie des Standardout der externen Shell. Die Befehlssubstitution erfasst alles, was auf Standard geschrieben ist, aus der Perspektive der darin enthaltenen Befehle. Da jedoch die Ausgabe von Befehl2 in Bezug auf die Befehlssubstitution in den Dateideskriptor 4 geht, wird sie durch die Befehlssubstitution nicht erfasst - jedoch einmal Wenn die Befehlssubstitution "beendet" wird, wird effektiv immer noch der gesamte Dateideskriptor 1 des Skripts aufgerufen.
(Das exec 4>&1
muss ein separater Befehl sein, da es vielen gängigen Shells nicht gefällt, wenn Sie versuchen, in einen Dateideskriptor innerhalb einer Befehlssubstitution zu schreiben, der im "externen" Befehl geöffnet wird, der die Substitution verwendet einfachste tragbare Möglichkeit, dies zu tun.)
Sie können es weniger technisch und spielerisch betrachten, als ob sich die Ausgaben der Befehle gegenseitig überspringen: Befehl1 leitet zu Befehl2 weiter, dann springt die Ausgabe des Drucks über Befehl 2, sodass Befehl2 ihn nicht abfängt, und dann Die Ausgabe von Befehl 2 springt über die Befehlssubstitution hinaus und verlässt sie, sobald printf gerade rechtzeitig landet, um von der Substitution erfasst zu werden, sodass sie in der Variablen landet, und die Ausgabe von Befehl2 wird auf fröhliche Weise in die Standardausgabe geschrieben in einem normalen Rohr.
Soweit ich weiß, $?
wird der Rückkehrcode des zweiten Befehls in der Pipe weiterhin enthalten sein, da Variablenzuweisungen, Befehlsersetzungen und zusammengesetzte Befehle für den Rückkehrcode des darin enthaltenen Befehls effektiv transparent sind, sodass der Rückgabestatus von Befehl2 sollte verbreitet werden - dies und das Fehlen einer zusätzlichen Funktion ist der Grund, warum ich denke, dass dies eine etwas bessere Lösung sein könnte als die von Lesmana vorgeschlagene.
Gemäß den Vorbehalten, die Lesmana erwähnt, ist es möglich, dass Befehl1 irgendwann die Dateideskriptoren 3 oder 4 verwendet. Um also robuster zu sein, würden Sie Folgendes tun:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Beachten Sie, dass ich in meinem Beispiel zusammengesetzte Befehle verwende, aber Subshells (die Verwendung von ( )
anstelle von { }
funktioniert auch, obwohl dies möglicherweise weniger effizient ist.)
Befehle erben Dateideskriptoren von dem Prozess, der sie startet, sodass die gesamte zweite Zeile den Dateideskriptor vier erbt und der zusammengesetzte Befehl gefolgt von 3>&1
dem Dateideskriptor drei. Das 4>&-
stellt also sicher, dass der innere zusammengesetzte Befehl den Dateideskriptor vier und den 3>&-
Dateideskriptor drei nicht erbt, sodass command1 eine sauberere, standardisiertere Umgebung erhält. Sie könnten auch das Innere 4>&-
neben das verschieben 3>&-
, aber ich denke, warum nicht einfach den Umfang so weit wie möglich einschränken.
Ich bin mir nicht sicher, wie oft die Dateien den Dateideskriptor drei und vier direkt verwenden. Ich denke, die meisten Programme verwenden Syscalls, die derzeit nicht verwendete Dateideskriptoren zurückgeben, aber manchmal schreibt Code direkt in den Dateideskriptor 3, I. Vermutung (Ich könnte mir vorstellen, dass ein Programm einen Dateideskriptor überprüft, um festzustellen, ob er geöffnet ist, und ihn verwendet, wenn dies der Fall ist, oder sich entsprechend anders verhält, wenn dies nicht der Fall ist). Letzteres ist daher wahrscheinlich am besten zu beachten und für allgemeine Fälle zu verwenden.