Considérez cet extrait:
$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz
Ici, j'ai mis $SOMEVAR
à AAA
sur la première ligne - et quand je le fais écho sur la deuxième ligne, j'obtiens le contenu AAA
comme prévu.
Mais alors, si j'essaye de spécifier la variable sur la même ligne de commande que le echo
:
$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz
... Je n'obtiens pas BBB
comme je m'y attendais - j'obtiens l'ancienne valeur (AAA
).
Est-ce ainsi que les choses devraient être? Si oui, comment se fait-il que vous puissiez alors spécifier des variables comme LD_PRELOAD=/... program args ...
et ça marche? Qu'est-ce que je rate?
Ce que vous voyez, c'est le comportement attendu. Le problème est que le shell parent évalue $SOMEVAR
sur la ligne de commande avant d'appeler la commande avec l'environnement modifié. Vous devez obtenir l'évaluation de $SOMEVAR
différé jusqu'à ce que l'environnement soit défini.
Vos options immédiates comprennent:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
.SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
.Ces deux utilisent des guillemets simples pour empêcher le shell parent d'évaluer $SOMEVAR
; il n'est évalué qu'après avoir été défini dans l'environnement (temporairement, pour la durée de la commande unique).
Une autre option consiste à utiliser la notation sous-Shell (comme le suggère également Marcus Kuhn dans son réponse ):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
La variable est définie uniquement dans le sous-shell
Franchement, le manuel prête à confusion sur ce point. Le manuel GNU Bash dit:
L'environnement de toute commande ou fonction simple [notez que cela exclut les prédéfinis] peut être augmenté temporairement en le préfixant avec des affectations de paramètres, comme décrit dans Paramètres du shell. Ces instructions d'affectation affectent uniquement l'environnement vu par cette commande.
Si vous analysez vraiment la phrase, cela signifie que l'environnement de la commande/fonction est modifié, mais pas l'environnement du processus parent. Donc, cela fonctionnera:
$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb
car l'environnement de la commande env a été modifié avant son exécution. Cependant, cela ne fonctionnera pas:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc
en raison du moment où l'expansion des paramètres est effectuée par le shell.
Une autre partie du problème est que Bash définit ces étapes pour son interprète:
Ce qui se passe ici, c'est que les builtins n'ont pas leur propre environnement d'exécution, donc ils ne voient jamais l'environnement modifié. De plus, les commandes simples (par exemple/bin/echo) obtiennent un environnement modifié (c'est pourquoi l'exemple env a fonctionné) mais l'expansion du Shell prend placer dans l'environnement actuel à l'étape 4.
En d'autres termes, vous ne passez pas 'aaa $ TESTVAR ccc' à/bin/echo; vous passez la chaîne interpolée (telle que développée dans l'environnement actuel) à/bin/echo. Dans ce cas, étant donné que l'environnement actuel n'a pas [~ # ~] testvar [~ # ~] , vous passez simplement 'aaa ccc' au commander.
La documentation pourrait être beaucoup plus claire. Heureusement qu'il y a débordement de pile!
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
Pour réaliser ce que vous voulez, utilisez
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
Raison:
Vous devez séparer l'affectation par un point-virgule ou une nouvelle ligne de la commande suivante, sinon elle n'est pas exécutée avant expansion des paramètres se produit pour la commande suivante (écho).
Vous devez effectuer l'affectation dans un environnement subshell, pour vous assurer qu'elle ne persiste pas au-delà de la ligne actuelle.
Cette solution est plus courte, plus soignée et plus efficace que certaines autres suggérées, en particulier elle ne crée pas de nouveau processus.
La raison en est que cela définit une variable d'environnement pour une ligne. Mais, echo
ne fait pas l'expansion, bash
le fait. Par conséquent, votre variable est réellement développée avant l'exécution de la commande, même si SOME_VAR
est BBB
dans le contexte de la commande echo.
Pour voir l'effet, vous pouvez faire quelque chose comme:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB
Ici, la variable n'est pas développée jusqu'à ce que le processus enfant s'exécute, vous voyez donc la valeur mise à jour. si vous cochez SOME_VARIABLE
à nouveau dans le shell parent, c'est toujours AAA
, comme prévu.
Voici une alternative:
SOMEVAR=BBB && echo zzz $SOMEVAR zzz
SOMEVAR=BBB; echo zzz $SOMEVAR zzz
Utiliser un ; pour séparer les instructions qui sont sur la même ligne.