Il est documenté (pour POSIX) dans la section 2.9.1 Commandes simples
des spécifications de base du groupe ouvert. Il y a un mur de texte là-bas; J'attire votre attention sur le dernier paragraphe:
S'il existe un nom de commande, l'exécution doit continuer comme décrit dans Recherche et exécution de commandes . S'il n'y a pas de nom de commande, mais que la commande contenait une substitution de commande, la commande doit se terminer avec l'état de sortie de la dernière substitution de commande effectuée. Sinon, la commande doit se terminer avec un état de sortie zéro.
Ainsi, par exemple,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
C'est aussi ainsi que bash fonctionne. Mais voir aussi la section «pas si simple» à la fin.
phk , dans sa question Les affectations sont comme des commandes avec un statut de sortie sauf quand il y a substitution de commande? , suggère
… Il semble que l'affectation elle-même compte comme une commande… avec une valeur de sortie nulle, mais qui s'applique avant le côté droit de l'affectation (par exemple, un appel de substitution de commande…)
Ce n'est pas une façon terrible de voir les choses. Un schéma brut pour la détermination du statut de retour d'une commande simple (ne contenant pas une ;
, &
, |
, &&
ou ||
) est:
- Parcourez la ligne de gauche à droite jusqu'à la fin ou un mot de commande (généralement un nom de programme).
- Si vous voyez une affectation de variable, le statut de retour de la ligne pourrait être 0.
- Si vous voyez une substitution de commande, c'est-à-dire,
$(…)
prenez le statut de sortie de cette commande.
- Si vous atteignez une commande réelle (pas dans une substitution de commande), prenez le statut de sortie de cette commande.
Le statut de retour de la ligne est le dernier numéro que vous avez rencontré.
Les substitutions de commandes comme arguments de la commande, par exemple, foo $(bar)
ne comptent pas; vous obtenez le statut de sortie de foo
. Pour paraphraser la notation de phk , le comportement ici est
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Mais c'est une légère simplification excessive. Le statut de retour global de
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
est l'état de sortie de . L' affectation qui se produit après l' affectation ne définit pas l'état de sortie global sur 0.
cmd4
E=
D=
icarus , dans sa réponse à la question de phk , soulève un point important: les variables peuvent être définies en lecture seule. L'avant-dernier paragraphe de la section 2.9.1 de la norme POSIX dit:
Si l'une des affectations de variables tente d'affecter une valeur à une variable pour laquelle l' attribut readonly est défini dans l'environnement shell actuel (indépendamment du fait que l'affectation soit effectuée dans cet environnement), une erreur d'affectation de variable se produit. Voir Conséquences des erreurs Shell pour les conséquences de ces erreurs.
donc si tu dis
readonly A
C=Garfield A=Felix T=Tigger
l'état de retour est 1. Peu importe si les chaînes Garfield
, Felix
et / ou Tigger
sont remplacés par substitution de commande (s) - mais voir les notes ci - dessous.
Section 2.8.1 Conséquences des erreurs de shell a un autre groupe de texte et un tableau et se termine par
Dans tous les cas indiqués dans le tableau où un shell interactif ne doit pas quitter, le shell ne doit effectuer aucun autre traitement de la commande dans laquelle l'erreur s'est produite.
Certains détails ont du sens; certains ne le font pas:
- L'
A=
affectation abandonne parfois la ligne de commande, comme cette dernière phrase semble le spécifier. Dans l'exemple ci-dessus, C
est défini sur Garfield
, mais T
n'est pas défini (et, bien sûr, aucun ne l'est A
).
- De même,
s'exécute
mais pas . Mais, dans mes versions de bash (qui comprennent 4.1.X et 4.3.X), il n'exécute . (Soit dit en passant, cela entrave encore l'interprétation de phk selon laquelle la valeur de sortie de l'affectation s'applique avant le côté droit de l'affectation.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Mais voici une surprise:
Dans mes versions de bash,
en lecture seule A
C = quelque chose A = quelque chose T = quelque chose cmd 0
n'exécute. En particulier,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
s'exécute
et , mais pas . (Notez que c'est l'opposé de son comportement quand il n'y a pas de commande.) Et il se positionne (ainsi que ) dans l'environnement de . Je me demande si c'est un bug dans bash.
cmd1
cmd3
cmd2
T
C
cmd0
Pas si simple:
Le premier paragraphe de cette réponse fait référence aux «commandes simples».
La spécification dit,
Une «commande simple» est une séquence d'affectations et de redirections de variables facultatives, dans n'importe quelle séquence, éventuellement suivie de mots et de redirections, terminée par un opérateur de contrôle.
Ce sont des déclarations comme celles de mon premier bloc d'exemple:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
les trois premiers incluent des affectations de variables et les trois derniers incluent des substitutions de commandes.
Mais certaines affectations de variables ne sont pas aussi simples.
bash (1) dit,
Déclarations d'affectation peuvent également apparaître comme arguments à la alias
, declare
, typeset
, export
, readonly
et local
builtin commandes ( déclaration des commandes).
Pour export
, la spécification POSIX dit,
ÉTAT DE SORTIE
0Tous les opérandes de nom ont été exportés avec succès.
> 0Au moins un nom n'a pas pu être exporté ou l' -p
option a été spécifiée et une erreur s'est produite.
Et POSIX ne prend pas en charge local
, mais bash (1) dit,
C'est une erreur à utiliser local
en dehors d'une fonction. L'état de retour est 0 sauf s'il local
est utilisé en dehors d'une fonction, si un nom non valide est fourni ou si le nom est une variable en lecture seule.
En lisant entre les lignes, nous pouvons voir que les commandes de déclaration comme
export FOO=$(bar)
et
local FOO=$(bar)
sont plus comme
foo $(bar)
dans la mesure où ils ignorent l'état de sortie bar
et vous donner un statut de sortie en fonction de la commande principale ( export
, local
ou foo
). Nous avons donc une étrangeté comme
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
que nous pouvons démontrer avec
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
et
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Heureusement, ShellCheck détecte l'erreur et déclenche SC2155 , qui indique que
export foo="$(mycmd)"
devrait être changé en
foo=$(mycmd)
export foo
et
local foo="$(mycmd)"
devrait être changé en
local foo
foo=$(mycmd)
local
liens avec cela? Par exemplelocal foo=$(bar)
?