Wie erfasst man den Beendigungsstatus eines nicht endgültigen Befehls in einer Pipeline?

Wie erfasst man den Beendigungsstatus eines nicht endgültigen Befehls in einer Pipeline?

Ich möchte den Beendigungsstatus eines Befehls erfassen, der irgendwo in einer Pipeline ausgeführt wirdVordie letzte Position. Wenn die Pipeline beispielsweise so etwas ist wie

command_1 ... | command_2 ... | command_3 ... | ... | command_n

... Ich möchte wissen, wie der Beendigungsstatus von command_1, oder von command_2, oder von command_3usw. erfasst wird. (Das Erfassen des Beendigungsstatus von command_nist natürlich trivial.)

Falls es von Bedeutung ist: Diese Pipeline erfolgt innerhalb einer ZSH-Shell-Funktion.


Ich habe versucht, den Beendigungsstatus command_1mit etwas wie zu erfassen

function_with_pipeline () {

    local command_1_status=-999999  # sentinel value

    { command_1 ...; command_1_status=$? } | command_2 ... | ... | command_n
    ...

}

...aber nach dem Ausführen der Pipeline war der Wert der command_1_statusVariablen immer noch der Sentinel-Wert.


Hier ist ein funktionierendes Beispiel, bei dem die Pipeline nur zwei Befehle hat:

foo ... | grep ...

fooist eine Funktion, die für dieses Beispiel wie folgt definiert ist:

foo () {

    (( $1 & 1 )) && echo "a non-neglible message"
    (( $1 & 2 )) && echo "a negligible message"
    (( $1 & 4 )) && echo "error message" >&2

    return $(( ( $1 & 4 ) >> 2 ))
}

Das Ziel besteht darin, den Beendigungsstatus des Aufrufs fooin der Pipeline zu erfassen.

Die Funktion function_with_pipelineimplementiert dazu die (letztlich wirkungslose) Strategie, die ich oben beschrieben habe:

function_with_pipeline () {

    local foo_status=-999999  # sentinel value

    { foo $1; foo_status=$? } | grep -v "a negligible message"

    printf '%d\ndesired: %d; actual: %d\n\n' $1 $(( ( $1 & 4 ) >> 2 )) $foo_status

}

Die folgende Schleife führt die function_with_pipelineFunktion aus. Die Ausgabe zeigt, dass der Wert der lokalen Variable foo_statusam Ende nicht anders ist als am Anfang.

for i in $(seq 0 7)
do
    function_with_pipeline $i
done
# 0
# desired: 0; actual: -999999
# 
# a non-neglible message
# 1
# desired: 0; actual: -999999
# 
# 2
# desired: 0; actual: -999999
# 
# a non-neglible message
# 3
# desired: 0; actual: -999999
# 
# error message
# 4
# desired: 1; actual: -999999
# 
# error message
# a non-neglible message
# 5
# desired: 1; actual: -999999
# 
# error message
# 6
# desired: 1; actual: -999999
# 
# error message
# a non-neglible message
# 7
# desired: 1; actual: -999999
#

Ich erhalte die gleichen Ergebnisse, wenn ich die localDeklaration in der Definition von weglasse foo_status.

Antwort1

pipestatusDafür gibt es ein spezielles Array in zsh. Versuchen Sie es also

command_1 ... | command_2 ... | command_3

Und

echo $pipestatus[1] $pipestatus[2] $pipestatus[3]

und der Grund, warum Ihr Ansatz nicht funktioniert, liegt darin, dass jede Pipe in einer separaten Untershell mit ihren eigenen Variablen ausgeführt wird, die zerstört werden, sobald Sie die Untershell verlassen.


Nur als Referenz, es steht PIPESTATUS(mit Großbuchstaben) in bash.

Antwort2

mispipefunktioniert in jeder Shell. Die Syntax (im Vergleich zu einer normalen Pipe) funktioniert folgendermaßen:

mispipe true false ; echo $?  # returns exit code of 1st command `true`
true | false ; echo $?  # returns exit code of 2nd command `false`

Ausgabe:

0
1

Was tun, wenn mehr als zwei Programme vorhanden sind:

# this still returns exit code of 1st command `true`
mispipe true 'false | false | false' ; echo $?

Ausgabe:

0

Obwohl kein sichtbares Zeichen vorhanden ist |, verhält es sich dennoch so, wie es sich für eine Pipe gehört:

yes | mispipe head 'wc -c'

Ausgabe:

     20

verwandte Informationen