web-dev-qa-db-fra.com

Pourquoi la définition d'une variable avant une commande est-elle légale dans bash?

Je viens de rencontrer plusieurs réponses telles que analyser un fichier texte délimité ... qui utilisent la construction:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

où la variable IFS est définie avant la commande read.

J'ai lu la référence bash mais je n'arrive pas à comprendre pourquoi c'est légal.

J'ai essayé

$ x="once upon" y="a time" echo $x $y

à partir de l'invite de commande bash, mais n'a obtenu aucun écho. Quelqu'un peut-il m'indiquer où cette syntaxe est définie dans la référence qui permet à la variable IFS d'être définie de cette manière? Est-ce un cas particulier ou puis-je faire quelque chose de similaire avec d'autres variables?

76
Mike Lippert

C'est sous Shell Grammar, Simple Commands (c'est moi qui souligne):

Une commande simple est une séquence d'affectations de variables facultatives suivie de mots et redirections séparés par des blancs, et terminée par un opérateur de contrôle. Le premier mot spécifie la commande à exécuter et est passé comme argument zéro. Les mots restants sont passés comme arguments à la commande invoquée.

Vous pouvez donc transmettre n'importe quelle variable que vous souhaitez. Votre exemple echo ne fonctionne pas car les variables sont passées à la commande, non définies dans le shell. Le Shell s'agrandit $x et $y avant d'invoquer la commande. Cela fonctionne, par exemple:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time
80
derobert

Les variables définies deviennent comme des variables d'environnement sur le processus forké.

Si vous courez

A="b" echo $A

bash développe d'abord $A en "" puis s'exécute

A="b" echo

Voici la bonne façon:

x="once upon" y="a time" bash -c 'echo $x $y'

Remarquez les guillemets simples dans bash -c, sinon vous avez le même problème que ci-dessus.

Votre exemple de boucle est donc légal car la commande bash de lecture intégrée recherchera IFS dans ses variables d'environnement et trouvera ,. Donc,

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

imprimera TEST is and I is test

Enfin, comme pour la syntaxe, dans une boucle for, une chaîne est attendue. J'ai donc dû utiliser des backticks pour en faire une commande. Cependant, alors que les boucles attendent une syntaxe de commande, telle que IFS=, read xx yy zz.

30
Mike Fairhurst

man bash

ENVIRONNEMENT

[...] L'environnement de toute commande ou fonction simple peut être temporairement augmenté en le préfixant avec des affectations de paramètres, comme décrit ci-dessus dans PARAMETRES. Ces instructions d'affectation affectent uniquement l'environnement vu par cette commande.

Les variables sont développées avant que l'affectation des variables n'ait lieu. Pour la raison évidente que var=x fonctionnerait également dans l'autre sens, mais var=$othervar ne le ferait pas. C'est à dire. votre $x est nécessaire avant d'être disponible. Mais ce n'est pas là le principal problème. Le problème principal est que la ligne de commande ne peut être modifiée que par l'environnement Shell mais que l'affectation ne fait pas partie de l'environnement Shell.

Vous mélangez les fonctionnalités: vous voulez un remplacement de ligne de commande mais placez la définition de variable dans l'environnement de commandes. Les remplacements de ligne de commande doivent être effectués par le shell. L'environnement doit être explicitement utilisé par la commande appelée. Que cela soit fait et comment cela dépend de la commande.

L'avantage de cette utilisation est que vous pouvez définir l'environnement d'un sous-processus sans affecter l'environnement Shell.

x="once upon" y="a time" bash -c 'echo $x $y'

fonctionne comme prévu car dans ce cas, les deux fonctionnalités sont combinées: Le remplacement de la ligne de commande n'est pas effectué par le shell appelant mais par le sous-processus Shell.

8
Hauke Laging

La commande que vous fournissez est différente car $x et $y sont développées avant la commande echo s'exécute, donc leurs valeurs dans le shell current sont utilisées, pas les valeurs que echo utiliseraient voir dans son environnement s'il devait regarder.

4
chepner

Je vais pour une plus grande image de "pourquoi c'est légal"

Réponse: Pour que vous puissiez appeler ou appeler un programme et pour cette invocation, utilisez uniquement une variable avec une variable particulière.

Par exemple: vous avez un paramètre pour une connexion à la base de données appelé 'db_connection', et normalement vous passez 'test' comme nom pour votre connexion à la base de données de test. En fait, vous pouvez même le définir par défaut que vous n'avez pas besoin de transmettre explicitement. Parfois, cependant, vous souhaitez travailler avec la base de données ci. Vous passez donc le paramètre en tant que "ci", puis le programme appelé utilise ce paramètre de base de données comme nom de la base de données à utiliser pour tous les appels de base de données . Pour la prochaine exécution, si vous ne répétez pas l'approche et appelez simplement le programme, la variable reviendra à sa valeur par défaut précédente.

2
Michael Durrant

Vous pouvez aussi utiliser ;. Il sera évalué avant car il s'agit d'un séparateur de commandes.

x="once upon" y="a time"; echo $x $y
0
camabeh