Pourquoi ne puis-je pas utiliser des variables comme préfixe d'une commande pour définir des variables d'environnement?

11

Normalement, il est possible de définir une variable d'environnement pour une commande en la préfixant comme ceci:

hello=hi bash -c 'echo $hello'

Je sais également que nous pouvons utiliser une variable pour remplacer n'importe quelle partie d'un appel de commande comme suit:

$ cmd=bash
$ $cmd -c "echo hi" # equivalent to bash -c "echo hi"

J'ai été très surpris de découvrir que vous ne pouvez pas utiliser une variable pour préfixer une commande pour définir une variable d'environnement. Cas de test:

$ prefix=hello=hi
$ echo $prefix # prints hello=hi
$ $prefix bash -c 'echo $hello'
hello=hi: command not found

Pourquoi ne puis-je pas définir la variable d'environnement à l'aide d'une variable? La partie préfixe est-elle une partie spéciale? J'ai pu le faire fonctionner en utilisant eval en face, mais je ne comprends toujours pas pourquoi. J'utilise bash 4.4.

wbkang
la source
1
Vous pouvez utiliser à la envplace de eval, lequel IIRC est plus sûr, mais plus lent.
wjandrea

Réponses:

14

Je soupçonne que c'est la partie de la séquence qui vous rattrape:

Les mots qui ne sont pas des affectations ou des redirections de variables sont développés (voir Extensions de shell). S'il reste des mots après l'expansion, le premier mot est considéré comme le nom de la commande et les mots restants sont les arguments

Cela provient du manuel de référence Bash dans la section sur l'extension de commande simple.

Dans l' cmd=bashexemple, aucune variable d'environnement n'est définie et bash traite la ligne de commande via l'expansion des paramètres, laissant bash -c "echo hi".

Dans l' prefix=hello=hiexemple, il n'y a à nouveau aucune affectation de variable lors de la première passe, donc le traitement continue l'expansion des paramètres, ce qui donne un premier mot de hello=hi.

Une fois que les affectations de variables ont été traitées, elles ne sont pas retraitées pendant l'exécution de la commande.

Voir le traitement et ses résultats sous set -x:

$ prefix=hello=hi
+ prefix=hello=hi
$ $prefix bash -c 'echo $hello'
+ hello=hi bash -c 'echo $hello'
-bash: hello=hi: command not found
$ hello=42 bash -c 'echo $hello'
+ hello=42
+ bash -c 'echo $hello'
42

Pour une variation plus sûre de "l'expansion variable" -en tant que "variables d'environnement" eval, considérez la suggestion de wjandreaenv :

prefix=hello=hi
env "$prefix" bash -c 'echo "$hello"'
hi

Ce n'est pas strictement une affectation de variable en ligne de commande, car nous utilisons la envfonction principale de l' utilitaire d'affecter des variables d'environnement à une commande, mais elle atteint le même objectif. La $prefixvariable est développée pendant le traitement de la ligne de commande, fournissant le nom = valeur à env, qui la transmet à bash.

Jeff Schaller
la source
3
De même, $foo=bar bash -c ...ne fonctionnera pas, car il $foo=barne s'agit pas d'une affectation de variable (quelle que soit la valeur de $foo), car $foo=barne correspond pas au name=valuemodèle d'affectation de variable.
muru
3

Parce que ce $prefixn'est pas une mission. @Jeff a l'explication la plus longue .

Vous pouvez faire une chose similaire avec une fonction à la place:

$ prefix() { hello=hi "$@"; }
$ prefix bash -c 'echo "$hello"'
hi

... et vous pouvez même les empiler si vous le souhaitez:

$ foo() { foo=123 "$@"; }
$ bar() { bar=456 "$@"; }
$ foo bar bash -c 'echo "$bar $foo"'
456 123
ilkkachu
la source