Les affectations sont comme des commandes avec un statut de sortie, sauf en cas de substitution de commande?

10

Voir les exemples suivants et leurs sorties dans des shells POSIX:

  1. false;echo $?ou false || echo 1:1
  2. false;foo="bar";echo $?ou foo="bar" && echo 0:0
  3. foo=$(false);echo $?ou foo=$(false) || echo 1:1
  4. foo=$(true);echo $?ou foo=$(true) && echo 0:0

Comme mentionné par la réponse la plus votée sur /programming/6834487/what-is-the-variable-in-shell-scripting :

$? est utilisé pour trouver la valeur de retour de la dernière commande exécutée.

C'est probablement un peu trompeur dans ce cas, alors obtenons la définition POSIX qui est également citée dans un article de ce thread:

? Se développe au statut de sortie décimal du pipeline le plus récent (voir Pipelines).

Il semble donc que l'affectation elle-même compte comme une commande (ou plutôt comme une partie de pipeline) avec une valeur de sortie nulle mais qui s'applique avant le côté droit de l'affectation (par exemple, la substitution de commandes appelle dans mes exemples ici).

Je vois comment ce comportement est logique d'un point de vue pratique, mais il me semble quelque peu inhabituel que l'affectation elle-même compte dans cet ordre. Peut-être pour expliquer plus clairement pourquoi c'est étrange pour moi, supposons que l'affectation était une fonction:

ASSIGNMENT( VARIABLE, VALUE )

alors foo="bar"serait

ASSIGNMENT( "foo", "bar" )

et foo=$(false)serait quelque chose comme

ASSIGNMENT( "foo", EXECUTE( "false" ) )

ce qui signifierait que les EXECUTEexécutions en premier et ensuite ASSIGNMENT sont exécutées mais c'est toujours le EXECUTEstatut qui compte ici.

Suis-je correct dans mon évaluation ou ai-je un malentendu / manque quelque chose? Est-ce que ce sont les bonnes raisons pour lesquelles je considère ce comportement comme "étrange"?

phk
la source
1
Désolé, mais je ne sais pas ce que vous trouvez étrange.
Kusalananda
1
@Kusalananda Peut-être que cela aide de vous dire que cela a commencé avec moi en me demandant: "Pourquoi false;foo="bar";echo $?retourne toujours 0 quand la dernière vraie commande qui a été exécutée false?" C'est fondamentalement que les affectations se comportent de manière spéciale en ce qui concerne les codes de sortie. Leur code de sortie est toujours 0, sauf quand ce n'est pas à cause de quelque chose qui s'est déroulé dans le côté droit de l'affectation.
phk

Réponses:

10

Le statut de sortie des affectations est étrange . La manière la plus évidente pour une affectation d'échouer est si la variable cible est marquée readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Notez que ni les chemins vrai ni faux de l'instruction if n'ont été pris, l'affectation échouant a arrêté l'exécution de l'instruction entière. bash en mode POSIX et ksh93 et ​​zsh abandonneront tous un script si une affectation échoue.

Pour citer la norme POSIX à ce sujet :

Une commande sans nom de commande, mais qui inclut une substitution de commande, a un état de sortie de la dernière substitution de commande effectuée par le shell.

C'est exactement la partie de la grammaire shell impliquée dans

 foo=$(err 42)

qui provient d'un simple_command(simple_command → cmd_prefix → ASSIGNMENT_WORD). Ainsi, si une affectation réussit, l'état de sortie est nul, sauf si une substitution de commande a été impliquée, auquel cas l'état de sortie est l'état de la dernière. Si l'affectation échoue, l'état de sortie est différent de zéro, mais vous ne pourrez peut-être pas l'attraper.

icare
la source
1
Pour ajouter à votre réponse, voici une réponse d'un thread différent où un nouveau standard POSIX à ce sujet est cité, la conclusion est fondamentalement la même: unix.stackexchange.com/a/270831/117599
phk
4

Vous dites,

… 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. 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.cmd4E=D=

De plus, comme le souligne icarus , les variables peuvent être définies en lecture seule. Considérez la variation suivante sur l'exemple d'icarus:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Même s'il Aest en lecture seule, bash exécute la substitution de commande à droite de A=- puis abandonne la commande car il Aest en lecture seule. Cela contredit en outre votre interprétation selon laquelle la valeur de sortie de l'affectation s'applique avant le côté droit de l'affectation.

G-Man dit «Réintègre Monica»
la source