Ich habe zwei Prozesse foound bar, verbunden mit einer Pipe:
$ foo | bar
barverlässt immer 0; Ich interessiere mich für den Exit-Code von foo. Gibt es eine Möglichkeit, daran heranzukommen?
Ich habe zwei Prozesse foound bar, verbunden mit einer Pipe:
$ foo | bar
barverlässt immer 0; Ich interessiere mich für den Exit-Code von foo. Gibt es eine Möglichkeit, daran heranzukommen?
Antworten:
Wenn Sie verwenden bash, können Sie die PIPESTATUSArray-Variable verwenden, um den Beendigungsstatus jedes Elements der Pipeline abzurufen.
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
Wenn Sie verwenden zsh, wird das Array aufgerufen pipestatus(Groß- und Kleinschreibung!) Und die Array-Indizes beginnen bei eins:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
So kombinieren Sie sie in einer Funktion auf eine Weise, bei der die Werte nicht verloren gehen:
$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0
Führen Sie den obigen Befehl in bashoder aus, zshund Sie erhalten die gleichen Ergebnisse. es wird nur eines von retval_bashund retval_zshgesetzt. Der andere wird leer sein. Dies würde es einer Funktion ermöglichen, mit zu enden return $retval_bash $retval_zsh(beachten Sie das Fehlen von Anführungszeichen!).
pipestatusin zsh. Leider haben andere Shells diese Funktion nicht.
echo "$pipestatus[1]" "$pipestatus[2]".
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
Es gibt drei Möglichkeiten, dies zu tun:
Die erste Möglichkeit besteht darin, die pipefailOption ( ksh, zshoder bash) festzulegen . Dies ist die einfachste und setzt im Grunde den Beendigungsstatus $?auf den Beendigungscode des letzten Programms, um das Programm zu beenden, der nicht Null ist (oder Null, wenn alle erfolgreich beendet wurden).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash hat auch eine Array-Variable namens $PIPESTATUS( $pipestatusin zsh), die den Exit-Status aller Programme in der letzten Pipeline enthält.
$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1
Sie können das dritte Befehlsbeispiel verwenden, um den bestimmten Wert in der Pipeline abzurufen, den Sie benötigen.
Dies ist die unhandlichste der Lösungen. Führen Sie jeden Befehl separat aus und erfassen Sie den Status
$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
ksh, aber von einem kurzen Blick auf die Manpage her unterstützt es nichts $PIPESTATUSoder ähnliches. Die pipefailOption wird jedoch unterstützt.
LOG=$(failed_command | successful_command)
Diese Lösung funktioniert ohne bash-spezifische Funktionen oder temporäre Dateien. Bonus: Am Ende ist der Beendigungsstatus tatsächlich ein Beendigungsstatus und keine Zeichenfolge in einer Datei.
Lage:
someprog | filter
Sie möchten den Exit-Status von someprogund die Ausgabe von filter.
Hier ist meine Lösung:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
Das Ergebnis dieses Konstrukts ist stdout von filterals stdout des Konstrukts und exit status von someprogals exit status des Konstrukts.
Dieses Konstrukt funktioniert auch mit einfacher Befehlsgruppierung {...}anstelle von Subshells (...). Subshells haben einige Auswirkungen, unter anderem einen Leistungsaufwand, den wir hier nicht benötigen. Weitere Informationen finden Sie im Handbuch zu Fine Bash: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
Leider erfordert die Bash-Grammatik Leerzeichen und Semikolons für die geschweiften Klammern, damit das Konstrukt viel geräumiger wird.
Für den Rest dieses Textes werde ich die Subshell-Variante verwenden.
Beispiel someprogund filter:
someprog() {
echo "line1"
echo "line2"
echo "line3"
return 42
}
filter() {
while read line; do
echo "filtered $line"
done
}
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
Beispielausgabe:
filtered line1
filtered line2
filtered line3
42
Hinweis: Der untergeordnete Prozess erbt die geöffneten Dateideskriptoren vom übergeordneten Prozess. Das bedeutet, someprogdass die offenen Dateideskriptoren 3 und 4 übernommen werden. Wenn someprogin Dateideskriptor 3 geschrieben wird, wird dies zum Beendigungsstatus. Der tatsächliche Beendigungsstatus wird ignoriert, da readnur einmal gelesen wird.
Wenn Sie befürchten, dass someprogin den Dateideskriptor 3 oder 4 geschrieben wird, sollten Sie die Dateideskriptoren vor dem Aufruf schließen someprog.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
Das exec 3>&- 4>&-Vorher someprogschließt den Dateideskriptor, bevor es ausgeführt wird, someprogsodass someprogdiese Dateideskriptoren einfach nicht vorhanden sind.
Es kann auch so geschrieben werden: someprog 3>&- 4>&-
Schritt für Schritt Erklärung des Konstrukts:
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
Von unten nach oben:
#part3) und rechts ( #part2) ausgeführt. exit $xsist auch der letzte Befehl der Pipe und das bedeutet, dass der String von stdin der Exit-Status des gesamten Konstrukts ist.#part2des gesamten Konstrukts hat.#part5und #part6) und rechts ( filter >&4) ausgeführt. Die Ausgabe von filterwird zu Dateideskriptor #part14 umgeleitet. In der Datei wurde Deskriptor 4 zu stdout umgeleitet. Dies bedeutet, dass die Ausgabe von filterdie Standardausgabe des gesamten Konstrukts ist.#part6wird in Dateideskriptor 3 gedruckt. In #part3Dateideskriptor 3 wurde umgeleitet #part2. Dies bedeutet, dass der Beendigungsstatus von #part6der endgültige Beendigungsstatus für das gesamte Konstrukt ist.someprogausgeführt wird. Der Exit-Status wird übernommen #part5. Die stdout wird von der Pipe aufgenommen #part4und an weitergeleitet filter. Die Ausgabe von filterwird wiederum stdout erreichen, wie in erläutert#part4(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)vereinfacht zu someprog 3>&- 4>&-.
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
Während nicht genau das, was Sie gefragt haben, könnten Sie verwenden
#!/bin/bash -o pipefail
damit Ihre Rohre die letzte Rückkehr ungleich Null zurückgeben.
könnte etwas weniger codierung sein
Edit: Beispiel
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefailinnerhalb des Skripts sollte robuster sein, zB für den Fall, dass jemand das Skript über ausführt bash foo.sh.
-o pipefailnicht in POSIX.
#!/bin/bash -o pipefail. Fehler ist:/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!Linien über die ersten, und so wird dies /bin/bash -o pipefail /tmp/ffanstelle der erforderlichen /bin/bash -o pipefail /tmp/ff- getopt(oder ähnlich) Analyse der Verwendung optarg, die in der Tagesordnung folgen ARGV, als Argument zu -o, so scheitert es. Wenn Sie einen Wrapper machen waren (sagen wir, bash-pfdass gerade tat exec /bin/bash -o pipefail "$@", und dass auf der Put - #!Linie, das funktionieren würde . Siehe auch: en.wikipedia.org/wiki/Shebang_%28Unix%29
Was ich mache, wenn möglich, ist, den Exit-Code von fooin einzugeben bar. Wenn ich zum Beispiel weiß, dass fooniemals eine Zeile mit nur Ziffern erzeugt wird, kann ich einfach den Exit-Code eingeben:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
Oder wenn ich weiß, dass die Ausgabe von foonie eine Zeile mit nur enthält .:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
Dies kann immer getan werden, wenn es eine Möglichkeit gibt, baralle außer der letzten Zeile zu bearbeiten und die letzte Zeile als Exit-Code zu übergeben.
Wenn bares sich um eine komplexe Pipeline handelt, deren Ausgabe Sie nicht benötigen, können Sie einen Teil davon umgehen, indem Sie den Beendigungscode in einem anderen Dateideskriptor drucken.
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
Danach $exit_codesist es normalerweise foo:X bar:Y, aber es könnte sein, bar:Y foo:Xdass es barbeendet wird, bevor alle Eingaben gelesen werden, oder wenn Sie Pech haben. Ich denke, dass Schreibvorgänge in Pipes mit bis zu 512 Bytes bei allen Unices atomar sind, sodass die foo:$?und bar:$?-Teile nicht gemischt werden, solange die Tag-Zeichenfolgen weniger als 507 Bytes umfassen.
Wenn Sie die Ausgabe von erfassen müssen bar, wird es schwierig. Sie können die oben genannten Techniken kombinieren, indem Sie dafür sorgen, dass die Ausgabe von barnever eine Zeile enthält, die wie eine Exit-Code-Anzeige aussieht, die jedoch unübersichtlich wird.
output=$(echo;
{ { foo; echo foo:"$?" >&3; } |
{ bar | sed 's/^/^/'; echo bar:"$?" >&3; }
} 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')
Natürlich gibt es auch die einfache Möglichkeit , den Status mithilfe einer temporären Datei zu speichern. Einfach, aber nicht so einfach in der Produktion:
/tmpist dies der einzige Ort, an dem ein Skript Dateien schreiben kann. Verwendung mktemp, die nicht POSIX ist, aber heutzutage auf allen seriösen Unices verfügbar ist.foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
Ausgehend von der Pipeline:
foo | bar | baz
Hier ist eine allgemeine Lösung, bei der nur die POSIX-Shell und keine temporären Dateien verwendet werden:
exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
(bar || echo "1:$?" >&3) |
(baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-
$error_statuses Enthält die Statuscodes aller fehlgeschlagenen Prozesse in zufälliger Reihenfolge mit Indizes, die angeben, welcher Befehl den jeweiligen Status ausgegeben hat.
# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2
# test if all commands succeeded:
test -z "$error_statuses"
# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null
Beachten Sie die Anführungszeichen $error_statusesin meinen Tests; ohne sie grepkann man nicht unterscheiden, weil die Zeilenumbrüche zu Leerzeichen gezwungen werden.
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 wird am besten von innen nach außen erklärt - command1 führt seine reguläre Ausgabe aus und druckt sie auf stdout (Dateideskriptor 1). Sobald dies erledigt ist, führt printf den Exit-Code von command1 aus und druckt ihn auf seiner stdout, aber diese stdout wird umgeleitet Dateideskriptor 3.
Während Befehl1 ausgeführt wird, wird seine stdout an Befehl2 weitergeleitet (die Ausgabe von printf führt niemals zu Befehl2, da wir sie an den Dateideskriptor 3 anstelle von 1 senden, den die Pipe liest). Dann leiten wir die Ausgabe von command2 an den Dateideskriptor 4 um, so dass sie auch außerhalb des Dateideskriptors 1 bleibt - weil wir wollen, dass der Dateideskriptor 1 ein wenig später frei ist, weil wir die printf-Ausgabe des Dateideskriptors 3 wieder in den Dateideskriptor zurückführen 1 - denn genau das wird von der Befehlsersetzung (den Backticks) erfasst und in die Variable eingefügt.
Das letzte bisschen Magie ist, dass exec 4>&1wir es zuerst als separaten Befehl gemacht haben - es öffnet den Dateideskriptor 4 als Kopie des stdout der externen Shell. Die Befehlsersetzung erfasst alles, was auf Standard geschrieben ist, aus der Perspektive der darin enthaltenen Befehle. Da die Ausgabe von Befehl2 jedoch für die Befehlsersetzung den Dateideskriptor 4 enthält, wird sie von der Befehlsersetzung nicht erfasst. Sobald die Befehlsersetzung "beendet" ist, wird der gesamte Dateideskriptor 1 des Skripts angezeigt.
(Das exec 4>&1muss ein separater Befehl sein, da es vielen gängigen Shells nicht gefällt, wenn Sie versuchen, in einen Dateideskriptor innerhalb einer Befehlsersetzung zu schreiben, der in dem "externen" Befehl geöffnet wird, der die Ersetzung verwendet einfachste tragbare Möglichkeit.)
Sie können es weniger technisch und spielerisch betrachten, als ob die Ausgaben der Befehle sich gegenseitig überspringen: command1 leitet zu command2, dann springt die Ausgabe von printf über command 2, damit command2 es nicht fängt, und dann Die Ausgabe von Befehl 2 springt über die Befehlsersetzung hinaus und verlässt sie, genau wie printf gerade noch rechtzeitig landet, um von der Ersetzung erfasst zu werden, sodass sie in der Variablen endet in einer normalen Pfeife.
Soweit ich weiß, $?wird es auch weiterhin den Rückgabecode des zweiten Befehls in der Pipe enthalten, da Variablenzuweisungen, Befehlsersetzungen und zusammengesetzte Befehle für den Rückgabecode des darin enthaltenen Befehls effektiv transparent sind, also den Rückgabestatus von befehl2 sollte weitergegeben werden - dies ist meiner meinung nach eine etwas bessere lösung als die von lesmana vorgeschlagene, da keine zusätzliche funktion definiert werden muss.
Gemäß den Vorbehalten, die Lesmana erwähnt, ist es möglich, dass command1 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 ( ( )anstelle von { }funktionieren 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 und der zusammengesetzte Befehl gefolgt von 3>&1den Dateideskriptor drei erbt. Das 4>&-stellt also sicher, dass der innere zusammengesetzte Befehl nicht den Dateideskriptor vier und der 3>&-Dateideskriptor drei erbt, sodass befehl1 eine 'sauberere', standardisiertere Umgebung erhält. Sie könnten auch das Innere 4>&-neben das bewegen 3>&-, aber ich denke, warum nicht einfach den Umfang so weit wie möglich einschränken.
Ich bin mir nicht sicher, wie oft die Dateideskriptoren drei und vier direkt verwendet werden - ich glaube, die meisten Programme verwenden Syscalls, die im Moment nicht verwendete Dateideskriptoren zurückgeben, aber manchmal schreibt Code direkt in Dateideskriptor 3 rate (Ich könnte mir vorstellen, dass ein Programm einen Dateideskriptor überprüft, um festzustellen, ob er geöffnet ist, und ihn verwendet, wenn er geöffnet ist, oder sich dementsprechend anders verhält, wenn er nicht geöffnet ist). Letzteres ist wahrscheinlich am besten zu beachten und für allgemeine Zwecke zu verwenden.
-bash: 3: Bad file descriptor.
Wenn Sie das Paket moreutils installiert haben, können Sie das Dienstprogramm mispipe verwenden, das genau das tut, was Sie gefragt haben.
Die obige Lösung von lesmana kann auch ohne den Mehraufwand für das Starten verschachtelter Unterprozesse mithilfe von ausgeführt werden { .. }(wobei zu beachten ist, dass diese Form von gruppierten Befehlen immer mit Semikolons abgeschlossen werden muss). Etwas wie das:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
Ich habe dieses Konstrukt mit Dash-Version 0.5.5 und Bash-Versionen 3.2.25 und 4.2.42 überprüft. Selbst wenn einige Shells keine { .. }Gruppierung unterstützen, ist es dennoch POSIX-kompatibel.
set -o pipefailin ksh oder einer beliebigen Anzahl gestreuter waitBefehle. Ich denke, dass es zumindest teilweise ein Parsing-Problem für ksh sein kann, als ob ich bei der Verwendung von Subshells bleibe, dann funktioniert es einwandfrei, aber selbst wenn ifman die Subshell-Variante für ksh wählt, aber die zusammengesetzten Befehle für andere belässt, schlägt dies fehl .
Dies ist portabel, funktioniert also mit jeder POSIX-kompatiblen Shell, erfordert nicht, dass das aktuelle Verzeichnis beschreibbar ist, und ermöglicht die gleichzeitige Ausführung mehrerer Skripts mit demselben Trick.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Edit: Hier ist eine stärkere Version, die Gilles 'Kommentaren folgt:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Edit2: und hier ist eine etwas leichtere Variante nach dubiousjim Kommentar:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)). @Johan: Ich stimme zu, dass es mit Bash einfacher ist, aber in manchen Zusammenhängen lohnt es sich, zu wissen, wie man Bash vermeidet.
Das Folgende ist als Zusatz zur Antwort von @Patrik gedacht, falls Sie eine der gebräuchlichen Lösungen nicht verwenden können.
Diese Antwort setzt Folgendes voraus:
$PIPESTATUSnoch kenntset -o pipefailZusätzliche Annahmen. Sie können alles loswerden, aber dies überfrachtet das Rezept zu sehr, so dass es hier nicht behandelt wird:
- Sie möchten lediglich wissen, dass alle Befehle in der PIPE den Beendigungscode 0 haben.
- Sie benötigen keine zusätzlichen Seitenbandinformationen.
- Ihre Shell wartet darauf, dass alle Pipe-Befehle zurückgegeben werden.
Vorher: foo | bar | bazgibt jedoch nur den Exit-Code des letzten Befehls zurück ( baz)
Gesucht: $?Muss nicht 0(true) sein, wenn einer der Befehle in der Pipe fehlgeschlagen ist
Nach:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
# $? now is 0 only if all commands had exit code 0
Erklärt:
mktemp. Dadurch wird normalerweise sofort eine Datei in erstellt/tmpwaitwird für benötigt ksh, da kshsonst nicht auf den Abschluss aller Pipe-Befehle gewartet wird. Beachten Sie jedoch, dass es unerwünschte Nebeneffekte gibt, wenn Hintergrundaufgaben vorhanden sind. Daher habe ich diese standardmäßig auskommentiert. Wenn das Warten nicht schmerzt, können Sie es in kommentieren.readzurückkehrt false, so truezeigt einen Fehler anDies kann als Plugin-Ersatz für einen einzelnen Befehl verwendet werden und erfordert nur Folgendes:
/proc/fd/NBUGs:
Dieses Skript hat einen Fehler, falls /tmpder Speicherplatz knapp wird. Wenn Sie Schutz gegen diese künstlichen Fall brauchen auch, können Sie es tun , wie folgt, was jedoch den Nachteil hat, dass die Anzahl der 0in 000der Anzahl von Befehlen in der Leitung hängt, so ist es etwas komplizierter:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
Hinweise zur Mobilität:
kshund ähnliche Shells, die nur auf den letzten Pipe-Befehl warten, brauchen das waitunkommentierte
Das letzte Beispiel wird printf "%1s" "$?"anstelle von verwendet, echo -n "$?"da dies portabler ist. Nicht jede Plattform interpretiert -nrichtig.
printf "$?"würde es auch tun, printf "%1s"fängt jedoch einige Eckfälle ab, falls Sie das Skript auf einer wirklich kaputten Plattform ausführen. (Lesen Sie: wenn Sie gerade programmieren paranoia_mode=extreme.)
FD 8 und FD 9 können auf Plattformen, die mehrere Ziffern unterstützen, höher sein. AFAIR eine POSIX-konforme Shell muss nur einzelne Ziffern unterstützen.
War 8.2 mit Debian getestet sh, bash, ksh, ash, sashund sogarcsh
Mit ein wenig Vorsicht sollte dies funktionieren:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
Der folgende 'if'-Block wird nur ausgeführt, wenn' command 'erfolgreich war:
if command; then
# ...
fi
Im Einzelnen können Sie Folgendes ausführen:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
Welches läuft haconf -makerwund speichert seine stdout und stderr zu "$ haconf_out". Wenn der zurückgegebene Wert von haconftrue ist, wird der ' grepif' -Block ausgeführt und lautet "$ haconf_out", wobei versucht wird, ihn mit "Cluster bereits beschreibbar" abzugleichen.
Beachten Sie, dass sich die Rohre automatisch selbst reinigen. Bei der Umleitung müssen Sie vorsichtig sein, um "$ haconf_out" zu entfernen, wenn Sie fertig sind.
Nicht so elegant wie pipefail, aber eine legitime Alternative, wenn diese Funktionalität nicht in Reichweite ist.
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"
exec 8>&- 9>&-
{
{
{
{ #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8 # use exactly 1x prior to #END
#END
} 2>&1 | ${TEE} 1>&9
} 8>&1
} | exit $(read; printf "${REPLY}")
} 9>&1
exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
(Mindestens mit bash) kann in Kombination mit set -esubshell verwendet werden, um Pipefail explizit zu emulieren und bei einem Pipe-Fehler zu beenden
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
Wenn foodies aus irgendeinem Grund fehlschlägt, wird der Rest des Programms nicht ausgeführt und das Skript wird mit dem entsprechenden Fehlercode beendet. (Dies setzt voraus, dass fooein eigener Fehler ausgegeben wird, der ausreicht, um die Fehlerursache zu verstehen.)
BEARBEITEN : Diese Antwort ist falsch, aber interessant, also lasse ich sie zum späteren Nachschlagen.
!zum Befehl wird der Rückkehrcode invertiert.
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls, nicht den Exit-Code von invertierenbogus_command