Ich versuche zu verstehen, wie der Ausgangsstatus kommuniziert wird, wenn eine Pipe verwendet wird. Angenommen, ich verwende which
, um ein nicht vorhandenes Programm zu finden:
which lss
echo $?
1
Da ich which
nicht gefunden habe, habe lss
ich den Exit-Status 1 erhalten. Das ist in Ordnung. Wenn ich jedoch Folgendes versuche:
which lss | echo $?
0
Dies zeigt an, dass der zuletzt ausgeführte Befehl normal beendet wurde. Der einzige Weg, dies zu verstehen, ist, dass PIPE möglicherweise auch einen Exit-Status erzeugt. Ist das der richtige Weg, es zu verstehen?
bash
shell-script
pipe
exit
sshekhar1980
quelle
quelle
which lss | echo $?
derecho
gestartet wird vor demwhich
Verlassen der ; Die Shell, für die die Befehlszeile generiertecho
wird, kann nicht wissen, wie der Exit-Statuswhich
später aussehen wird.Antworten:
Der Exit-Status der Pipeline ist der Exit-Status des letzten Befehls in der Pipeline (es sei denn, die Shell-Option
pipefail
ist in den Shells festgelegt, die ihn unterstützen. In diesem Fall ist der Exit-Status der des letzten Befehls in der Pipeline, der mit beendet wird ein Status ungleich Null).Es ist nicht möglich, den Exit-Status einer Pipeline innerhalb einer Pipeline auszugeben , da es keine Möglichkeit gibt, den Wert zu ermitteln, bis die Ausführung der Pipeline tatsächlich abgeschlossen ist.
Die Pipeline
ist unsinnig, da
echo
die Standardeingabe nicht gelesen wird (Pipelines werden verwendet, um Daten zwischen der Ausgabe eines Befehls und der Eingabe des nächsten zu übergeben). Dasecho
würde auch nicht den Exit-Status der Pipeline drucken, sondern den Exit-Status des Befehls, der unmittelbar vor der Pipeline ausgeführt wird.Dies ist ein besseres Beispiel:
Dies bedeutet, dass eine Pipeline auch verwendet werden kann mit
if
:quelle
Der Ausgangsstatus einer Pipe ist der Ausgangsstatus des rechten Befehls. Der Exit-Status des linken Befehls wird ignoriert.
(Beachten Sie, dass
which lss | echo $?
dies nicht angezeigt wird. Sie würden es ausführenwhich lss | true; echo $?
, um dies anzuzeigen. Inwhich lss | echo $?
gibtecho $?
den Status des letzten Befehls vor dieser Pipeline an.)Der Grund, warum sich Shells so verhalten, ist, dass es ein ziemlich häufiges Szenario gibt, in dem ein Fehler auf der linken Seite ignoriert werden sollte. Wenn die rechte Seite beendet wird (oder im Allgemeinen ihre Standardeingabe schließt), während die linke Seite noch schreibt, empfängt die linke Seite ein SIGPIPE-Signal. In diesem Fall ist normalerweise nichts falsch: Die rechte Seite kümmert sich nicht um die Daten; Wenn die Rolle der linken Seite ausschließlich darin besteht, diese Daten zu erzeugen, kann sie gestoppt werden.
Wenn jedoch die linke Seite aus einem anderen Grund als SIGPIPE stirbt oder wenn die Aufgabe der linken Seite nicht nur darin bestand, Daten zur Standardausgabe zu erzeugen, ist ein Fehler auf der linken Seite ein echter Fehler sollte gemeldet werden.
Im Klartext besteht die einzige Lösung darin, ein benanntes Rohr zu verwenden.
In ksh, bash und zsh können Sie die Shell anweisen, einen Pipeline-Exit mit einem Status ungleich Null durchzuführen, wenn eine Komponente der Pipeline mit einem Status ungleich Null beendet wird. Sie müssen die
pipefail
Option einstellen :set -o pipefail
shopt -s pipefail
setopt pipefail
(odersetopt pipe_fail
)In mksh, bash und zsh können Sie den Status jeder Komponente der Pipeline mithilfe der Variablen
PIPESTATUS
(bash, mksh) oderpipestatus
(zsh) abrufen. Hierbei handelt es sich um ein Array, das den Status aller Befehle in der letzten Pipeline enthält (eine Verallgemeinerung von$?
).quelle
Ja, PIPE erzeugt einen Exit-Status. Wenn Sie den Exit-Code des Befehls vor dem PIPE haben möchten, können Sie ihn verwenden
$PIPESTATUS
Gemäß diesen Fragen und Antworten zum Stapelüberlauf können Sie Folgendes tun, um den Beendigungsstatus eines Befehls zu drucken, wenn Sie eine Pipe verwenden:
Oder setzen Sie Pipefail mit dem Befehl
set -o pipefail
(um es zu deaktivieren , tun Sie esset +o pipefail
) und verwenden Sie$?
stattdessen$PIPESTATUS
.Diese Befehle funktionieren nur, nachdem Sie sie mindestens einmal ausgeführt haben. Das bedeutet, dass es
PIPESTATUS
nur funktioniert, wenn Sie es nach dem Befehl mit der Pipe wie folgt ausführen:Sie erhalten 10 für
${PIPESTATUS[0]}
und 1 für${PIPESTATUS[1]}
. Weitere Informationen finden Sie in diesen U & L-Fragen und Antworten.quelle
PIPESTATUS
Ihr erstes Beispiel den Exit-Status der Befehle in der letzten Pipeline enthält, ist es nicht sinnvoller als dassomecmd | echo $?
, worauf Kusalananda in seiner Antwort eingegangen ist.false; true | echo $PIPESTATUS
druckt1
für den Exit-Status vonfalse
.|exho
ist wirklich verwirrendDie Shell wertet die Befehlszeile aus, bevor die Pipeline ausgeführt wird. Dies
$?
gilt auch für den Wert des letzten Befehls, der vor der Pipeline ausgeführt wurde. Obecho
selbst vorher oder nachher gestartetwhich
wird, spielt keine Rolle.Wenn Ihre Frage speziell die Weitergabe des Exit-Status betraf, können Sie das Standardverhalten folgendermaßen untersuchen:
Wie Sie sehen können, ist der Exit-Status einer Pipeline der Exit-Status ihres letzten Befehls.
quelle