Ich bin verwirrt darüber, welchen Fehlercode der Befehl zurückgibt, wenn eine Variablenzuweisung einfach und mit Befehlsersetzung ausgeführt wird:
a=$(false); echo $?
Es gibt 1
aus, was mich zu der Überzeugung bringt, dass die Zuweisung der Variablen beim letzten nicht durchläuft oder einen neuen Fehlercode erzeugt. Aber als ich das ausprobierte:
false; a=""; echo $?
Es gibt 0
aus, offensichtlich gibt a=""
dies zurück und überschreibt 1
, der von false
zurückgegeben wird.
Ich möchte wissen, warum dies geschieht. Gibt es irgendwelche Besonderheiten bei der Variablenzuweisung, die sich von anderen normalen Befehlen unterscheiden? Oder einfach nur, weil a=$(false)
als ein einzelner Befehl betrachtet wird und nur der Befehlssubstitutionsteil sinnvoll ist?
- UPDATE -
Vielen Dank an alle, aus den Antworten und Kommentaren habe ich den Punkt erhalten "Wenn Sie eine Variable mithilfe der Befehlsersetzung zuweisen, ist der Beendigungsstatus der Status des Befehls." (von @Barmar), diese Erklärung ist hervorragend klar und leicht verständlich, aber sprechen Sie für Programmierer nicht genau genug. Ich möchte die Referenz dieses Punktes von Behörden wie TLDP oder der Manpage GNU sehen Hilf mir, es herauszufinden, danke nochmal!
Wenn ein Befehl als $(command)
ausgeführt wird, kann sich die Ausgabe des Befehls selbst ersetzen .
Wenn du sagst:
a=$(false) # false fails; the output of false is stored in the variable a
die vom Befehl false
erzeugte Ausgabe wird in der Variablen a
gespeichert. Darüber hinaus ist der Exit-Code derselbe wie der vom Befehl erzeugte. help false
würde sagen:
false: false
Return an unsuccessful result.
Exit Status:
Always fails.
Auf der anderen Seite sagen:
$ false # Exit code: 1
$ a="" # Exit code: 0
$ echo $? # Prints 0
bewirkt, dass der Exit-Code für die Zuweisung zu a
zurückgegeben wird, der 0
ist.
BEARBEITEN:
Zitat aus dem Handbuch :
Wenn eine der Erweiterungen eine Befehlsersetzung enthielt, ist der Beendigungsstatus des Befehls der Beendigungsstatus der zuletzt ausgeführten Befehlsersetzung.
Zitat aus BASHFAQ/002 :
Wie kann ich den Rückgabewert und/oder die Ausgabe eines Befehls in einer Variablen speichern?
...
output=$(command)
status=$?
Die Zuweisung zu
output
hat keine Auswirkung auf den Beendigungsstatus voncommand
, der sich noch in$?
befindet.
Beachten Sie, dass dies nicht der Fall ist, wenn Sie local
innerhalb einer Funktion verwenden. Das Verhalten unterscheidet sich geringfügig von dem in der akzeptierten Antwort beschriebenen Verhalten und dem hier veröffentlichten Link: http://mywiki.wooledge.org/BashFAQ/002
Nehmen Sie zum Beispiel dieses Bash-Skript:
#!/bin/bash
function funWithLocalVar() {
local output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
function funWithoutLocalVar() {
output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
funWithLocalVar
funWithoutLocalVar
Hier ist die Ausgabe davon:
[email protected]:~$ ./tmp.sh
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1
Vielleicht kümmert es niemanden, aber ich habe es getan. Ich brauchte eine Minute, um herauszufinden, warum mein Statuscode immer 0 war, wenn es manchmal nicht der Fall war. Nicht zu 100% klar warum. Aber nur zu wissen, dass dies geholfen hat.
Ich bin gestern (29. August 2018) auf dasselbe Problem gestoßen.
Zusätzlich zu local
in Nick Ps Antwort und @ sevkos Kommentar in der akzeptierten Antwort , hat declare
im globalen Bereich dasselbe Verhalten.
Hier ist mein Bash-Code:
#!/bin/bash
func1()
{
ls file_not_existed
local local_ret1=$?
echo "local_ret1=$local_ret1"
local local_var2=$(ls file_not_existed)
local local_ret2=$?
echo "local_ret2=$local_ret2"
local local_var3
local_var3=$(ls file_not_existed)
local local_ret3=$?
echo "local_ret3=$local_ret3"
}
func1
ls file_not_existed
global_ret1=$?
echo "global_ret1=$global_ret1"
declare global_var2=$(ls file_not_existed)
global_ret2=$?
echo "global_ret2=$global_ret2"
declare global_var3
global_var3=$(ls file_not_existed)
global_ret3=$?
echo "global_ret3=$global_ret3"
Die Ausgabe:
$ ./declare_local_command_substitution.sh 2>/dev/null
local_ret1=2
local_ret2=0
local_ret3=2
global_ret1=2
global_ret2=0
global_ret3=2
Beachten Sie die Werte von local_ret2
und global_ret2
in der obigen Ausgabe. Die Exit-Codes werden von local
und declare
überschrieben.
Meine Bash-Version:
$ echo $BASH_VERSION
4.4.19(1)-release
(keine Antwort auf die ursprüngliche Frage, aber zu lang für einen Kommentar)
Beachten Sie, dass export A=$(false); echo $?
0 ausgibt! Anscheinend gelten die in devnulls Antwort zitierten Regeln nicht mehr. So fügen Sie diesem Zitat ein wenig Kontext hinzu (Hervorhebung meines):
3.7.1 Einfache Befehlserweiterung
...
Wenn nach der Erweiterung noch ein Befehlsname vorhanden ist, wird die Ausführung wie unter unten beschrieben ausgeführt. Andernfalls wird der Befehl beendet. Wenn eine der Erweiterungen eine Befehlsersetzung enthielt, ist der Beendigungsstatus des Befehls der Beendigungsstatus der zuletzt durchgeführten Befehlsersetzung. Wenn keine Befehlssubstitutionen vorhanden waren, wird der Befehl mit dem Status Null beendet.
3.7.2 Befehlssuche und Ausführung [- dies ist der Fall "unter"]
IIUC beschreibt das Handbuch var=foo
als Sonderfall der var=foo command...
-Syntax (ziemlich verwirrend!). Die Regel "Beendigungsstatus der letzten Befehlssubstitution" gilt nur für den Fall ohne Befehl.
Es ist zwar verführerisch, export var=foo
als "modifizierte Zuweisungssyntax" zu betrachten, es ist jedoch nicht - export
ist ein eingebauter Befehl (der zufällig zuweisungsähnliche Argumente benötigt).
=> Wenn Sie den Ersetzungsstatus eines var AND-Erfassungsbefehls exportieren möchten, führen Sie ihn in zwei Schritten aus:
A=$(false)
# ... check $?
export A
Dieser Weg funktioniert auch im set -e
-Modus - wird sofort beendet, wenn die Befehlsersetzung nicht 0 zurückgibt.