97 votes

Comment puis-je obtenir la sortie et la valeur de sortie d'un sous-shell lors de l'utilisation de "bash -e"?

Considérez le code suivant

outer-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "Je pensais que j'allais mourir :("

inner-scope.sh

#!/bin/bash
function inner() { echo "gagnant"; return 1; }

J'essaie de faire en sorte que outer-scope.sh se termine lorsque l'appel à inner() échoue. Étant donné que $() invoque un sous-shell, cela ne se produit pas.

Comment puis-je obtenir la sortie d'une fonction tout en préservant le fait que la fonction peut se terminer avec un code de sortie non nul ?

140voto

James Mertz Points 390

$() préserve le code de sortie; vous devez simplement l'utiliser dans une instruction qui n'a pas de code de sortie propre, comme une affectation.

output=$(inner)

Après cela, $? contiendrait le code de sortie de inner, et vous pouvez utiliser toutes sortes de vérifications à ce sujet:

output=$(inner) || exit $?
echo $output

Ou:

if ! output=$(inner); then
    exit $?
fi
echo $output

Ou:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(Remarque : Un simple exit sans arguments est équivalent à exit $? – c'est-à-dire, il sort avec le code de sortie de la dernière commande. J'ai utilisé la deuxième forme uniquement pour plus de clarté.)


Aussi, pour information : source est complètement sans rapport dans ce cas. Vous pouvez simplement définir inner() dans le fichier outer-scope.sh, avec les mêmes résultats.

44voto

ryenus Points 849

Voir BashFAQ/002:

Si vous voulez à la fois (la sortie et le statut de sortie) :

output=$(command)
status=$? 

Un Cas Spécial

Notez un cas délicat avec les variables locales de fonction, comparez le code suivant :

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

Nous obtiendrons :

$ f     # trompé par 'local' avec une initialisation en ligne
output:data, status:0

$ g     # bon cas
output:data, status:1

Pourquoi?

En bash, local est en fait une commande intégrée. Lorsque la sortie d'un sous-shell est utilisée pour initialiser une variable local, le statut de sortie n'est plus celui du sous-shell, mais celui de la commande locale, qui est 0 tant que la variable locale est créée.

Voir également https://stackoverflow.com/a/4421282/537554

4voto

Daniel Beck Points 105590
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "J'ai pensé que j'aurais pu mourir :("

En ajoutant echo, le sous-shell ne se trouve pas seul (n'est pas vérifié séparément) et n'interrompt pas. L'assignation contourne ce problème.

Vous pouvez aussi faire ceci, et rediriger la sortie vers un fichier, pour la traiter ultérieurement.

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X