Verlaat code van variabele toewijzing om vervanging commando te geven in Bash

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 1uit, 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 0af, uiteraard is dit wat a=""retourneert en het overschrijft 1geretourneerd 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 falsewordt opgeslagen in de variabele a. Bovendien is de exit-code dezelfde als die door de opdracht wordt geproduceerd. help falsezou 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 awordt geretourneerd die 0is.


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 outputheeft geen effect op de exit-status van command, 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 commandis 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:

[email protected]:~$ ./tmp.sh 
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1

Dit komt omdat localeigenlijk een ingebouwde opdracht is, en een opdracht zoals local variable="$(command)"roept localnaaan nadat de uitvoer van commandis vervangen. U krijgt dus de uitgangsstatus van local.


Antwoord 3, autoriteit 7%

Ik kwam gisteren hetzelfde probleem tegen (29 augustus 2018).

In aanvulling op localgenoemd in Nick P.’s antwoorden @sevko’s opmerking in het geaccepteerde antwoord, heeft declarein 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_ret2en global_ret2in de bovenstaande uitvoer. De exit-codes worden overschreven door localen 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=fooals 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=foote zien als een “aangepaste toewijzingssyntaxis”, is dat niet zo – exportis 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.

Other episodes