Comment définir une variable d'environnement sur la ligne de commande et l'afficher dans les commandes?

152

Si je cours

export TEST=foo
echo $TEST

Il produit foo.

Si je cours

TEST=foo echo $TEST

Ce ne est pas. Comment puis-je obtenir cette fonctionnalité sans utiliser export ou un script?

ashleysmithgpu
la source
Attention, cette histoire ne se résume pas à ce qu’elle semble au départ. Je vous invite à vérifier ma réponse.
jasonleonhard

Réponses:

180

Cela est dû au fait que le shell développe la variable dans la ligne de commande avant d' exécuter la commande et que la variable n'existe pas à ce moment-là. Si tu utilises

TEST=foo; echo $TEST

ça va marcher.

exportfera apparaître la variable dans l'environnement des commandes exécutées ultérieurement (pour savoir comment cela fonctionne à bash, voir help export). Si vous avez seulement besoin que la variable apparaisse dans l'environnement d'une commande, utilisez ce que vous avez essayé, à savoir:

TEST=foo your-application
Peterph
la source
1
Qu'en est-il / usr / bin / env? ça ne marche pas non plus, tu sais pourquoi?
Benubird
5
La même raison - le shell développe $TESTavant que la ligne de commande ne soit exécutée. Une fois que l ' echoest en cours d' exécution (notez également que echocela se traduira généralement par la commande intégrée du shell et non par /bin/echo), il voit la variable définie dans son environnement. Cependant, echo $TESTne dit pas echode sortir le contenu de la variable TESTdepuis son environnement. Il demande au shell de fonctionner echoavec l'argument comme étant tout ce qui se trouve actuellement dans la variable appelée TEST- et ce sont deux choses très différentes.
Peter
@peterph Si shell développe la variable dans la ligne de commande avant d'exécuter la commande, pourquoi ne la développe-t-elle pas var=value sh -c 'echo "$var"'?
haccks
Comme je vous l’ai expliqué ici : c’est parce que les variables sont développées entre guillemets doubles (par exemple, "… $var …") mais pas entre guillemets simples (par exemple, '… $var …'). Étant donné qu'il echo "$var"s'agit de guillemets simples, cette chaîne entière est transmise au sh -cshell new ( ) sans être interprétée par le shell externe et interactif. … (Suite)
G-Man
(Suite)… Comme igal l'a dit hier , il y a deux cycles d'analyse syntaxique - bien que je crois que igal a mal interprété votre question. Il n'y a pas deux cycles d'analyse en dehors de l'analyse effectuée par le shell parent. Il y a deux cycles d'analyse: une effectuée par le shell externe (parent) interactif et une par le sh -cshell enfant new ( ).
G-Man
39

Je soupçonne que vous voulez que les variables shell aient une portée limitée, plutôt que les variables d'environnement. Les variables d'environnement sont une liste de chaînes passées aux commandes lorsqu'elles sont exécutées .

Dans

var=value echo whatever

Vous transmettez la var=valuechaîne à l'environnement que l'écho reçoit. Cependant, echone fait rien avec sa liste d'environnement et de toute façon, dans la plupart des shells, echoest construit et n'est donc pas exécuté .

Si tu avais écrit

var=value sh -c 'echo "$var"'

Cela aurait été une autre affaire. Ici, nous passons var=valueà la shcommande et shutilisons son environnement. Coquilles convertissent chacune des variables qu'ils reçoivent de leur environnement à une variable shell, de sorte que la varvariable d'environnement shreçoit sera converti en une $varvariable et quand il se dilate dans cette echoligne de commande, qui deviendront echo value. Parce que l'environnement est par défaut hérité, echorecevra également var=valuedans son environnement (ou le serait s'il était exécuté), mais encore une fois, echone se soucie pas de l'environnement.

Maintenant, si comme je le soupçonne, ce que vous voulez, c'est limiter la portée des variables du shell, plusieurs approches sont possibles.

Portable (Bourne et POSIX):

(var=value; echo "1: $var"); echo "2: $var"

Le (...) ci-dessus démarre un sous-shell (un nouveau processus shell dans la plupart des shells), ainsi toute variable y déclarée n'affectera que ce sous-shell, aussi je m'attendrais à ce que le code ci-dessus génère "1: valeur" et "2:" ou "2: quel que soit ce que var a été configuré avant".

Avec la plupart des shells de type Bourne, vous pouvez utiliser des fonctions et la fonction "locale":

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

Avec zsh, vous pouvez utiliser des fonctions inline:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

ou:

function { local var=value; echo "1: $var"; }; echo "2: $var"

Avec bash et zsh (mais pas ash, pdksh ou AT & T ksh), cette astuce fonctionne aussi:

var=value eval 'echo "1: $var"'; echo "2: $var"

Une variante qui fonctionne en quelques coquilles (plus dash, mksh, yash) mais pas zsh(sauf dans sh/ kshémulation):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(utiliser commanddevant un shell spécial (ici eval) dans les shells POSIX supprime leur particularité (ici, les affectations de variables d'entre eux restent en vigueur après leur retour))

Stéphane Chazelas
la source
Pour mes besoins, c'est la solution correcte. Je ne veux pas que la variable 'var' pollue l'environnement comme c'est le cas dans la réponse acceptée.
kronenpj
6

Vous le faites correctement, mais la syntaxe bash est facile à interpréter de manière erronée: vous pouvez penser que cela echo $TESTprovoque echode récupérer TESTenv var puis de l’imprimer, ce n’est pas le cas. Donc donné

export TEST=123

ensuite

TEST=456 echo $TEST

implique la séquence suivante:

  1. Le shell analyse toute la ligne de commande et exécute toutes les substitutions de variables. La ligne de commande devient alors

    TEST=456 echo 123
  2. Il crée les vars temporaires définis avant la commande. Il enregistre la valeur actuelle de TESTet la remplace par 456; la ligne de commande est maintenant

    echo 123
  3. Il exécute la commande restante, qui dans ce cas affiche 123 sur stdout (la commande shell restante n’a même pas utilisé la valeur temp de TEST)

  4. Il restaure la valeur de TEST

Utilisez plutôt printenv, car cela n'implique pas de substitution de variable:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>
Oliver
la source
+1 J'ai trouvé printenvutile de tester / prouver le concept (se comporte comme un script, par opposition à echo)
Daryn
5

Vous pouvez obtenir ce travail en utilisant:

TEST=foo && echo $TEST
pradeepchhetri
la source
6
Dans ce cas, TEST=foos'exécute en tant qu'instruction distincte - elle n'est pas simplement définie dans le contexte de echo.
Codeforester