web-dev-qa-db-fra.com

Code de sortie de l'affectation de variable à la substitution de commande dans Bash

Je ne comprends pas quel code d'erreur la commande retournera lors de l'exécution d'une affectation de variable de manière claire et avec substitution de commande:

a=$(false); echo $?

Il génère 1, ce qui me laisse penser que l’affectation de variable ne balaye pas ou ne génère pas de nouveau code d’erreur sur le dernier. Mais quand j'ai essayé ceci:

false; a=""; echo $?

Il génère 0, ce qui est évidemment ce que a="" retourne et il remplace 1 renvoyé par false.

Je veux savoir pourquoi cela se produit, y a-t-il une particularité dans l'affectation de variable qui diffère des autres commandes normales? Ou juste parce que a=$(false) est considéré comme une commande unique et que seule une partie de substitution de commande a un sens?

-- METTRE À JOUR --

Merci à tous. D'après les réponses et les commentaires, je comprends que "lorsque vous affectez une variable à l'aide d'une substitution de commande, le statut de sortie correspond à celui de la commande." (par @Barmar), cette explication est extrêmement claire et facile à comprendre, mais le langage n'est pas assez précis pour les programmeurs, je souhaite voir la référence de ce point par des autorités telles que TLDP ou la page de manuel GNU, aidez-moi à le découvrir, merci encore!

41
Reorx

Notez que ce n'est pas le cas lorsque vous utilisez local dans une fonction. Ce qui est un comportement légèrement différent de celui décrit dans la réponse acceptée et le lien affiché ici: http://mywiki.wooledge.org/BashFAQ/002

Prenez ce script bash par exemple:

#!/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

Voici le résultat de ceci:

nick.parry@nparry-laptop1:~$ ./tmp.sh 
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1

Peut-être que personne ne s'en soucie, mais c'est ce que j'ai fait. Il m'a fallu une minute pour comprendre pourquoi mon code de statut était toujours 0 alors que ce n'était clairement pas parfois. Pas 100% clair pourquoi. Mais le simple fait de savoir que cela a aidé.

6
Nick P.

Je suis tombé sur le même problème hier (29 août 2018).

En plus de local mentionné dans la réponse de Nick P. et le commentaire de @ sevko dans la réponse acceptée , declare au niveau global ont également le même comportement.

Voici mon code Bash:

#!/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"

Le résultat:

$ ./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

Notez les valeurs de local_ret2 et global_ret2 dans le résultat ci-dessus. Les codes de sortie sont remplacés par local et declare.

Ma version de Bash:

$ echo $BASH_VERSION 
4.4.19(1)-release
1
Zhi Zhu

(pas une réponse à la question initiale mais trop longue pour un commentaire)

Notez que export A=$(false); echo $? génère 0! Apparemment, les règles citées dans la réponse de devnull ne s'appliquent plus. Pour ajouter un peu de contexte à cette citation (c'est moi qui souligne):

3.7.1 Extension de commande simple

...

S'il reste un nom de commande après le développement, l'exécution se poursuit comme décrit ci-dessous. Sinon, la commande se ferme. Si l'une des extensions contenait une substitution de commande, l'état de sortie de la commande est celui de la dernière substitution de commande effectuée. S'il n'y a aucune substitution de commande, la commande se termine avec un statut égal à zéro.

3.7.2 Recherche et exécution de commande [- c'est le cas "ci-dessous"]

Le manuel IIUC décrit var=foo comme cas particulier de la syntaxe var=foo command... (assez déroutant!). La règle "statut de sortie de la dernière substitution de commande" s'applique uniquement au cas de non-commande. 

Bien qu'il soit tentant de penser à export var=foo en tant que "syntaxe d'assignation modifiée", ce n'est pas - export est une commande intégrée (qui prend juste des arguments semblables à ceux d'une affectation).

=> Si vous souhaitez exporter un statut de substitution de commande var AND capture, procédez en 2 étapes:

A=$(false)
# ... check $?
export A

Cette méthode fonctionne également en mode set -e - quitte immédiatement si la substitution de commande retourne une valeur autre que 0.

0