valeur temporaire du script bash à la commande

11

Comme la commande ci-dessous,

if true; then
   IFS=":" read a b c d e f <<< "$test"

Le livre dit que lorsque la commande d'assignation de valeur ( IFS ":") est utilisée avant la commande principale ( read a b c d e f <<< "$value"), sa valeur est temporairement appliquée à la commande principale. Ainsi, la readcommande utilise un délimiteur :.

Mais, comme cette commande,

if true; then
   HOME="hello" echo "$HOME"

Le message d'écho n'est pas bonjour. Quelle est la vraie signification de la commande ci-dessus?

A.Cho
la source

Réponses:

5

Cela revient à une question sur le fonctionnement de l'évaluation. Les deux exemples fonctionnent de la même manière, le problème se produit en raison de la façon dont le shell (bash, ici) développe les variables.

Lorsque vous écrivez cette commande:

HOME="foo" echo $HOME

Le $HOMEest dilatée avant la commande est exécutée . Par conséquent, il est étendu à la valeur d'origine et non à la nouvelle valeur que vous avez définie pour la commande. La HOMEvariable a en effet été modifiée dans l'environnement dans lequel la echocommande s'exécute, cependant, vous imprimez le $HOMEdepuis le parent.

Pour illustrer, considérez ceci:

$ HOME="foo" bash -c 'echo $HOME'
foo
$ echo $HOME
/home/terdon

Comme vous pouvez le voir ci-dessus, la première commande imprime la valeur temporairement modifiée de HOMEet la seconde imprime l'original, démontrant que la variable n'a été modifiée que temporairement. Étant donné que la bash -c ...commande est placée entre guillemets simples ( ' ') au lieu de doubles ( " "), la variable n'est pas développée et est transmise telle quelle au nouveau processus bash. Ce nouveau processus le développe ensuite et imprime la nouvelle valeur à laquelle il a été défini. Vous pouvez voir cela se produire si vous utilisez set -x:

$ set -x
$ HOME="hello" echo "$HOME"
+ HOME=hello         
+ echo hello
hello

Comme vous pouvez le voir ci-dessus, la variable $HOME n'est jamais passée à echo. Il ne voit que sa valeur étendue. Comparer avec:

$ HOME="hello" bash -c 'echo $HOME'
+ HOME=hello
+ bash -c 'echo $HOME'
hello

Ici, en raison des guillemets simples, la variable et non sa valeur sont transmises au nouveau processus.

terdon
la source
7

Lorsque le shell analyse une ligne, il tokenise la ligne en mots, effectue diverses extensions (dans l'ordre) sur les mots, puis exécute la commande.

Supposer test=1:2:3:4:5:6

Regardons cette commande: IFS=":" read a b c d e f <<< "$test"

Après la tokenisation et l' expansion des paramètres se produit:IFS=":" read a b c d e f <<< "1:2:3:4:5:6"

Le shell définira la variable IFS pendant la durée de la commande de lecture et readsait comment appliquer $ IFS à son entrée et donner des valeurs aux noms de variable.

Cette commande a une histoire similaire, mais un résultat différent: HOME="hello" echo "$HOME"

Étant donné que l'expansion des paramètres se produit avant le début de la commande, le shell a:

HOME="hello" echo "/home/username"

Et puis, lors de l'exécution de la commande echo, la nouvelle valeur de $ HOME n'est pas du tout utilisée.

Pour réaliser ce que vous essayez de faire, choisissez l'un des

# Delay expansion of the variable until its new value is set
HOME="hello" eval 'echo "$HOME"'

ou

# Using a subshell, so the altered env variable does not affect the parent.
# The semicolon means that the variable assignment will occur before
# the variable expansion
(HOME="hello"; echo "$HOME")

mais ne choisissez pas le premier.

glenn jackman
la source
Il vaut probablement mieux choisir le premier. Au moins, c'est beaucoup plus rapide. Lorsque eval est la réponse, vous posez parfois la mauvaise question. Mais si quelqu'un doit le faire pour certaines raisons, changer la réponse ne rend pas la question elle-même moins fausse. Une autre solution consiste à l'envelopper dans une fonction et à l'utiliser local.
user23013
Pourquoi evaléviter la solution?
DarkHeart
Si vous ne contrôlez pas strictement l'entrée, vous devez faire très attention au code que vous autorisez d'autres personnes à injecter dans votre programme.
glenn jackman
-1

Il existe deux étendues: les variables d'environnement et les variables locales. Les variables d'environnement sont valides pour chaque processus (voir setenv, getenv), tandis que les variables locales ne sont actives que dans cette session shell. (Ce n'est pas une distinction évidente ...)

Impliqué env(comme dans votre exemple) modifie l'environnement, tout en echo ...utilisant les locaux - envn'a donc aucun effet.

Pour modifier les variables locales, utilisez, par exemple,

( HOME="foo" ; echo "$HOME" )

Ici, les parenthèses définissent la portée de cette affectation.

Andrew Miloradovsky
la source
1
Cela n'a rien à voir avec l'étendue des variables, le problème est que la variable est développée avant d'être passée au shell enfant.
terdon