Nach diesem Referenzhandbuch :
-E (auch -o errtrace)
Wenn diese Option aktiviert ist, werden alle Traps in ERR von Shell-Funktionen, Befehlsersetzungen und Befehlen übernommen, die in einer Subshell-Umgebung ausgeführt werden. Die ERR-Falle wird in solchen Fällen normalerweise nicht vererbt.
Ich muss es jedoch falsch interpretieren, da Folgendes nicht funktioniert:
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
Ich kenne schon einfache Zuordnung, my_var=$(made_up_name)
werde das Skript mit verlassen set -e
(also errexit).
Soll -E/-o errtrace
das wie der obige Code funktionieren? Oder habe ich es höchstwahrscheinlich falsch gelesen?
bash
command-substitution
dgo.a
quelle
quelle
echo $( made up name )
durch$( made up name )
erzeugt das gewünschte Verhalten. Ich habe jedoch keine Erklärung.var=$( pipe )
und$( pipe )
Beispiele würden also beide Pipe-Endpunkte darstellen, wohingegenpipe > echo
dies nicht der Fall wäre. Meine Manpage sagt: "1. Der Ausfall eines einzelnen Befehls in einerecho
immer 0 zurückgegeben wird. Dies muss in der Analyse berücksichtigt werden ...Antworten:
Hinweis:
zsh
Beschwert sich über "schlechte Muster", wenn Sie für die meisten Beispiele hier keine "Inline-Kommentare" zulassen und diese nicht wie bisher über eine Proxy-Shell ausführensh <<-\CMD
.Okay, also, wie ich in den obigen Kommentaren angegeben habe, weiß ich nicht genau, was bash ist
set -E
, aber ich weiß, dass POSIX-kompatible Shells ein einfaches Mittel zum Testen eines Werts darstellen, wenn Sie es wünschen:Oben sehen Sie werden feststellen , dass wenn ich verwendet
parameter expansion
zu testen , um${empty?} _test()
nochreturn
s ein Pass - wie in der letzten bekundete wirdecho
Dies geschieht , weil der ausgefallene Wert der tötet$( command substitution )
Subshell , die es enthält, aber seine Eltern Schale -_test
zu diesem Zeitpunkt - hält auf Lkw - Transporte. Undecho
es ist mir egal - es ist reichlich glücklich, nur zu dienen,\newline; echo
ist kein Test.Aber bedenke folgendes:
Da ich die
_test()'s
Eingabe mit einem vorab ausgewerteten Parameter gespeist habe , versuchtINIT here-document
die_test()
Funktion überhaupt nicht, sie auszuführen. Außerdem gibt diesh
Shell den Geist anscheinend ganz auf undecho "this doesnt even print"
druckt nicht einmal.Wahrscheinlich ist , dass nicht , was Sie wollen.
Dies geschieht, weil die
${var?}
style parameter-expansion so konzipiert ist, dass sieshell
im Falle eines fehlenden Parameters beendet wird. Dies funktioniert folgendermaßen :Ich werde nicht das gesamte Dokument kopieren / einfügen, aber wenn Sie einen Fehler für einen
set but null
Wert wünschen, verwenden Sie das Formular:Mit dem
:colon
wie oben. Wenn einnull
Wert erfolgreich sein soll, lassen Sie den Doppelpunkt weg. Sie können es auch negieren und nur für festgelegte Werte fehlschlagen, wie ich gleich zeigen werde.Ein weiterer Lauf von
_test():
Dies funktioniert mit allen Arten von Schnelltests. Oben sehen Sie jedoch, dass die
_test()
Ausführung ab der Mitte des Fehlerspipeline
und die darin enthaltenecommand list
Subshell vollständig fehlschlägt, da keiner der Befehle in der Funktion ausgeführt wird und der folgendeecho
überhaupt nicht ausgeführt wird. es wird aber auch gezeigt, dass es einfach getestet werden kann, da esecho "now it prints"
jetzt druckt.Der Teufel steckt wohl im Detail. Im obigen Fall ist die Shell, die beendet wird, nicht die des Skripts,
_main | logic | pipeline
sondern die( subshell in which we ${test?} ) ||
, für die ein wenig Sandboxing erforderlich ist.Und es mag nicht offensichtlich sein, aber wenn Sie nur für den umgekehrten Fall oder nur für
set=
Werte übergehen wollten , ist es auch ziemlich einfach:Das obige Beispiel nutzt alle 4 Formen der POSIX-Parametersubstitution und ihre verschiedenen
:colon null
odernot null
Tests. Es gibt mehr Informationen im obigen Link und hier ist es wieder .Und ich denke, wir sollten auch unsere
_test
Funktionsarbeit zeigen , oder? Wir deklarieren nurempty=something
als Parameter für unsere Funktion (oder jederzeit vorher):Es ist zu beachten, dass diese Bewertung für sich genommen ist - es ist kein zusätzlicher Test erforderlich, um fehlzuschlagen. Noch ein paar Beispiele:
Und so kommen wir schließlich zur ursprünglichen Frage zurück: Wie
$(command substitution)
gehe ich mit Fehlern in einer Subshell um? Die Wahrheit ist - es gibt zwei Möglichkeiten, aber keine ist direkt. Der Kern des Problems ist der Evaluierungsprozess der Shell - Shell-Erweiterungen (einschließlich$(command substitution)
) treten im Evaluierungsprozess der Shell früher auf als die aktuelle Ausführung von Shell-Befehlen - und dies ist der Zeitpunkt, an dem Ihre Fehler abgefangen und abgefangen werden könnten.Das Problem, das bei der
$(command substitution)
Operation auftritt, ist , dass die Subshell zum Zeitpunkt der Auswertung der aktuellen Shell auf Fehler bereits ersetzt wurde - es verbleiben keine Fehler.Also, was sind die beiden Möglichkeiten? Entweder machen Sie es explizit in der
$(command substitution)
Subshell mit Tests, als würden Sie es ohne, oder Sie absorbieren die Ergebnisse in eine aktuelle Shell-Variable und testen ihren Wert.Methode 1:
Methode 2:
Dies schlägt unabhängig von der Anzahl der pro Zeile deklarierten Variablen fehl:
Und unser Rückgabewert bleibt konstant:
JETZT DIE FALLE:
quelle
echo "abc" "${v1:?}"
das Programm scheinbar nicht ausgeführt wird (abc wurde nie gedruckt). Und die Shell gibt 1 zurück. Dies gilt mit oder ohne Befehl even ("${v1:?}"
direkt auf der CLI). Für das OP-Skript war es jedoch nur erforderlich, seine Variablenzuweisung, die einen Ersatz für einen nicht vorhandenen Befehl enthält, in eine einzelne Zeile zu setzen. Ansonsten ist das Verhalten des Echos immer 0 zurückzugeben, sofern es nicht wie bei den von Ihnen erläuterten Tests unterbrochen wurde.v=$( madeup )
. Ich sehe nicht, was daran unsicher ist. Es ist nur eine Aufgabe, die Person hat zum Beispiel den Befehl falsch geschriebenv="$(lss)"
. Es ist ein Fehler. Ja können Sie mit dem Fehlerstatus des letzten Befehls $ überprüfen? - weil es der Befehl in der Zeile ist (eine Zuweisung ohne Befehlsnamen) und sonst nichts - kein Argument zum Echo. Außerdem wird es hier von der Funktion als! = 0 abgefangen, sodass Sie zweimal eine Rückmeldung erhalten. Ansonsten gibt es sicher, wie Sie erklären, eine bessere Möglichkeit, dies innerhalb eines Rahmens zu ordnen, aber OP hatte eine einzige Zeile: ein Echo plus seine fehlgeschlagene Substitution. Er wunderte sich über das Echo.In Ihrem Skript ist es eine Befehlsausführung (
echo $( made up name )
). In Bash-Befehlen wird entweder mit ; oder mit neuer Zeile . Im Befehl$( made up name )
wird als Teil des Befehls betrachtet. Selbst wenn dieser Teil fehlschlägt und mit einem Fehler zurückkehrt, wird der gesamte Befehl erfolgreich ausgeführt, da erecho
nichts davon weiß. Wenn der Befehl mit 0 zurückkehrt, wird kein Trap ausgelöst.Sie müssen es in zwei Befehle, Zuweisung und Echo, setzen
quelle
echo $var
nicht scheitern -$var
wird erweitert, um leer zu sein, bevor man es sich überhauptecho
ansieht.Dies ist auf einen Fehler in der Bash zurückzuführen. Während der Befehlsersetzung treten Sie ein
made
wird in einer Subshell ausgeführt (oder nicht gefunden), die Subshell ist jedoch so "optimiert", dass sie keine Überfüllungen der übergeordneten Shell verwendet. Dies wurde in Version 4.4.5 behoben :Mit bash 4.4.5 oder höher sollten Sie die folgende Ausgabe sehen:
Der Trap-Handler wurde wie erwartet aufgerufen, und die Subshell wird beendet. (
set -e
Bewirkt nur, dass die Subshell beendet wird, nicht die übergeordnete, sodass die Nachricht "sollte nicht erreicht werden" tatsächlich erreicht werden sollte.)Eine Problemumgehung für ältere Versionen besteht darin, die Erstellung einer vollständigen, nicht optimierten Subshell zu erzwingen:
Die zusätzlichen Räume werden benötigt von Arithmetic Erweiterung zu unterscheiden.
quelle