Ik ben in de war over welke foutcode de opdracht zal retourneren wanneer een variabele toewijzing duidelijk en met opdrachtvervanging wordt uitgevoerd:
a=$(false); echo $?
Het voert 1
uit, wat me doet denken dat variabele toewijzing niet veegt of nieuwe foutcode produceert bij de laatste. Maar toen ik dit probeerde:
false; a=""; echo $?
Het geeft 0
af, uiteraard is dit wat a=""
retourneert en het overschrijft 1
geretourneerd door false
.
Ik wil weten waarom dit gebeurt, is er een bijzonderheid in de toewijzing van variabelen die verschilt van andere normale opdrachten? Of gewoon omdat a=$(false)
wordt beschouwd als een enkele opdracht en alleen het vervangen van een opdracht zinvol is?
— BIJWERKEN —
Bedankt iedereen, uit de antwoorden en opmerkingen begreep ik het punt “Als je een variabele toewijst met behulp van opdrachtvervanging, is de exit-status de status van de opdracht.” (door @Barmar), deze uitleg is uitstekend duidelijk en gemakkelijk te begrijpen, maar spreekt niet precies genoeg voor programmeurs, ik wil de referentie van dit punt zien van autoriteiten zoals de TLDP- of GNU-manpagina, help me het te vinden uit, nogmaals bedankt!
Antwoord 1, autoriteit 100%
Bij het uitvoeren van een opdracht als $(command)
staat de uitvoer van het commando om zichzelf te vervangen.
Als je zegt:
a=$(false) # false fails; the output of false is stored in the variable a
de output geproduceerd door het commando false
wordt opgeslagen in de variabele a
. Bovendien is de exit-code dezelfde als die door de opdracht wordt geproduceerd. help false
zou vertellen:
false: false
Return an unsuccessful result.
Exit Status:
Always fails.
Aan de andere kant zeggen:
$ false # Exit code: 1
$ a="" # Exit code: 0
$ echo $? # Prints 0
zorgt ervoor dat de afsluitcode voor de toewijzing aan a
wordt geretourneerd die 0
is.
BEWERKEN:
Citaat uit de handleiding:
Als een van de uitbreidingen een opdrachtvervanging bevat, is de exit
status van de opdracht is de uitgangsstatus van de laatste opdracht
vervanging uitgevoerd.
Citaat uit BASHFAQ/002:
Hoe kan ik de retourwaarde en/of uitvoer van een commando opslaan in a
variabel?…
output=$(command)
status=$?
De toewijzing aan
output
heeft geen effect op de exit-status vancommand
, die
is nog steeds in$?
.
Antwoord 2, autoriteit 25%
Merk op dat dit niet het geval is in combinatie met local
, zoals in local variable="$(command)"
. Dat formulier wordt succesvol afgesloten, zelfs als command
is mislukt.
Neem bijvoorbeeld dit Bash-script:
#!/bin/bash
function funWithLocalAndAssignmentTogether() {
local output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
function funWithLocalAndAssignmentSeparate() {
local output
output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
funWithLocalAndAssignmentTogether
funWithLocalAndAssignmentSeparate
Hier is de uitvoer hiervan:
nick.parry@nparry-laptop1:~$ ./tmp.sh
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1
Dit komt omdat local
eigenlijk een ingebouwde opdracht is, en een opdracht zoals local variable="$(command)"
roept local
naaan nadat de uitvoer van command
is vervangen. U krijgt dus de uitgangsstatus van local
.
Antwoord 3, autoriteit 7%
Ik kwam gisteren hetzelfde probleem tegen (29 augustus 2018).
In aanvulling op local
genoemd in Nick P.’s antwoorden @sevko’s opmerking in het geaccepteerde antwoord, heeft declare
in globale scope ook hetzelfde gedrag.
Hier is mijn 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"
De uitvoer:
$ ./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
Let op de waarden van local_ret2
en global_ret2
in de bovenstaande uitvoer. De exit-codes worden overschreven door local
en declare
.
Mijn Bash-versie:
$ echo $BASH_VERSION
4.4.19(1)-release
Antwoord 4, autoriteit 4%
(geen antwoord op de oorspronkelijke vraag maar te lang voor commentaar)
Merk op dat export A=$(false); echo $?
geeft 0! Blijkbaar zijn de regels in antwoord van devnullniet langer van toepassing. Om wat context aan dat citaat toe te voegen (nadruk van mij):
3.7.1 Eenvoudige opdrachtuitbreiding
…
Als er een opdrachtnaam over is na de uitbreiding, gaat de uitvoering verder zoals hieronderbeschreven. Anderswordt het commando afgesloten. Als een van de uitbreidingen een opdrachtvervanging bevatte, is de exit-status van de opdracht de exit-status van de laatst uitgevoerde opdrachtvervanging. Als er geen opdrachtvervangingen zijn, wordt de opdracht afgesloten met de status nul.
3.7.2 Opdracht zoeken en uitvoeren [ — dit is het “hieronder” geval]
IIUC de handleiding beschrijft var=foo
als speciaal geval van var=foo command...
syntaxis (behoorlijk verwarrend!). De regel “uitgangsstatus van de laatste opdrachtvervanging” is alleen van toepassing op het geval zonder opdracht.
Hoewel het verleidelijk is om export var=foo
te zien als een “aangepaste toewijzingssyntaxis”, is dat niet zo – export
is een ingebouwd commando (dat gebeurt gewoon neem opdrachtachtige argumenten).
=> Als u de vervangingsstatus van een var AND capture-opdracht wilt exporteren, doet u dit in 2 fasen:
A=$(false)
# ... check $?
export A
Deze manier werkt ook in de modus set -e
— wordt onmiddellijk afgesloten als de opdrachtvervanging niet-0 retourneert.