Si j'exécute cette command bash et que je préfixe l'instruction, de sorte que le fruit
la variable doit exister, mais seulement pour la durée de cette command:
$ fruit=apple echo $fruit $
Le résultat est une ligne vide. Pourquoi?
Pour citer un commentaire de wildcard sur cette question :
l'expansion des parameters est effectuée par le shell, et la variable "fruit" n'est pas une variable shell; c'est seulement une variable d'environnement dans l'environnement de la command "echo"
- checkbashisms-conforme façon de déterminer la coquille actuelle
- Comment écrire un file à différents décalages vers une partition de système de files avec la command dd
- Le choix de Bash: opérateur vs mot réservé
- Imprimez des valeurs de données d'image Exif spécifiques avec exiv2
- Comment convertir tous les files .wav dans des sous-directorys en .aac en utilisant neroAacEnc?
Une variable d'environnement est toujours une variable, donc sûrement cela devrait toujours être disponible pour la command echo?
Pour bien comprendre cela, commençons par différencier les variables shell des variables d' environnement.
Les variables d'environnement sont une propriété de TOUS les process, qu'ils les utilisent en interne ou non. Même sleep 10
quand il est en cours d'exécution a des variables d'environnement. De même que tous les process ont un PID (identificateur de process), un directory de travail courant (cwd), un PPID (parent PID), une list d'arguments (même si elle est vide), etc. De même, tous les process ont ce qu'on appelle un «environnement», qui est hérité du process parent quand il fourche.
Du sharepoint vue de l'auteur de l'utilitaire (quelqu'un qui écrit du code en C), les process ont la possibilité de définir, d'annuler ou de modifier les variables d'environnement. Cependant, du sharepoint vue d'un auteur de script, la plupart des outils n'offrent pas cette fonctionnalité à leurs users. Au lieu de cela, vous utilisez votre shell pour modifier l'environnement de process qui est ensuite hérité lorsque la command (binary externe) que vous avez appelée est exécutée. (L'environnement de votre shell lui-même peut être modifié et la modification héritée, ou vous pouvez diriger votre shell pour effectuer la modification après le forçage mais avant d'exécuter la command que vous avez appelée. approches.)
Les variables shell sont une autre chose. Bien qu'à l' intérieur de la coquille ils se comportent de la même manière, la différence est que de simples "variables shell" ne modifient pas ou n'influencent pas le comportement des commands que vous appelez de votre shell. Dans une terminologie appropriée, la distinction serait en fait énoncée un peu différemment; les variables shell exscopes feront partie de l'environnement des outils que vous appelez, alors que les variables shell qui ne sont pas exscopes ne le seront pas. Cependant, il me semble plus utile que la communication fasse reference à des variables shell qui ne sont pas exscopes en tant que «variables shell» et aux variables shell qui sont exscopes en tant que «variables d'environnement» parce que ce sont des variables d'environnement du sharepoint vue des process.
C'est beaucoup de text. Regardons quelques exemples et décrivons ce qui se passe:
$ somevar=myfile $ ls -l "$somevar" -rw-r--r-- 1 Myname staff 0 May 29 19:12 myfile $
Dans cet exemple, somevar
est juste une variable shell, rien de spécial à ce sujet. L' expansion du paramètre shell (voir LESS='+/Parameter Expansion' man bash
) se produit avant que l'exécutable ls
soit réellement chargé ("exec" ed) et la command ls
(process) ne voit jamais la string "dollar sign somevar". Il ne voit que la string "monfile", interprète cela comme le path vers un file dans le directory de travail courant, et obtient et imprime des informations à ce sujet.
Si nous export somevar
avant la command ls
, le fait que somevar=myfile
apparaisse dans l' environnement du process ls
, mais cela n'affectera rien car la command ls
ne fera rien avec cette variable. Pour voir l' effet d'une variable d'environnement, nous devons choisir une variable d'environnement que le process que nous appelons vérifiera et fera quelque chose avec.
bc: Calculasortingce de base
Il y a peut-être un meilleur exemple, mais c'est celui que j'ai trouvé ce n'est pas trop compliqué. D'abord, vous devez savoir que bc
est une calculasortingce de base, et traite et calcule des expressions mathématiques. (Après avoir traité le contenu de tous les files d'input, il traite son input standard.Je ne vais pas utiliser son input standard pour mes exemples, je vais juste appuyer sur Ctrl-D, qui ne montrera pas dans les extraits de text ci-dessous. J'utilise -q
pour supprimer un message d'introduction sur chaque invocation.)
La variable d'environnement que je vais illustrer est décrite dans man bc
:
BC_ENV_ARGS This is another mechanism to get arguments to bc. The format is the same as the command line arguments. These arguments are processed first, so any files listd in the environment argu- ments are processed before any command line argument files. This allows the user to set up "standard" options and files to be processed at every invocation of bc. The files in the envi- ronment variables would typically contain function definitions for functions the user wants defined every time bc is run.
Voici:
$ cat file1 5*5 $ bc -q file1 25 $ cat file2 6*7 8+9+10 $ bc -q file2 42 27 $ bc -q file1 file2 25 42 27 $
C'est juste pour montrer comment fonctionne bc
. Dans chacun de ces cas, j'ai dû appuyer sur Ctrl-D pour signaler "fin d'input" à bc
.
Passons maintenant une variable d'environnement directement à bc
:
$ BC_ENV_ARGS=file1 bc -q file2 25 42 27 $ echo "$BC_ENV_ARGS" $ bc -q file2 42 27 $
Notez que ce que nous mettons dans cette variable n'est pas visible plus tard à partir d'une command echo
. En plaçant l'assignation dans la même command (sans point-virgule non plus), nous avons placé cette assignation de variable dans l'environnement de bc
-it, laissant le shell lui-même non affecté.
BC_ENV_ARGS
maintenant BC_ENV_ARGS
comme variable shell :
$ BC_ENV_ARGS=file1 $ echo "$BC_ENV_ARGS" file1 $ bc -q file2 42 27 $
Ici vous pouvez voir que notre command echo
peut voir le contenu, mais cela ne fait pas partie de l'environnement de bc
donc bc
ne peut rien faire de spécial.
Bien sûr, si nous mettons la variable elle-même dans la list des arguments de BC, nous verrons quelque chose:
$ bc -q "$BC_ENV_ARGS" 25 $
Mais ici, c'est le shell qui étend la variable, puis file1
est ce qui apparaît dans la list des arguments de bc
. Donc, il l'utilise toujours comme une variable shell, pas une variable d'environnement.
Maintenant, exportons cette variable, donc c'est à la fois une variable shell ET une variable d'environnement:
$ export BC_ENV_ARGS $ echo "$BC_ENV_ARGS" file1 $ bc -q file2 25 42 27 $
Et ici vous pouvez voir que file1
est traité avant le file2
, même s'il n'est pas mentionné sur la command line ici. Cela fait partie de l'environnement du shell et devient partie intégrante de l'environnement de bc
lors de l'exécution de ce process, de sorte que la valeur de cette variable d'environnement est héritée et affecte le fonctionnement de bc
.
Nous pouvons toujours substituer ceci par command, même le substituer à une valeur vide:
$ BC_ENV_ARGS= bc -q file2 42 27 $ echo "$BC_ENV_ARGS" file1 $ bc -q file2 25 42 27 $
Mais comme vous pouvez le voir, la variable rest définie et exscope dans notre shell, visible à la fois pour le shell lui-même et pour les commands bc
ultérieures qui ne surchargent pas la valeur. Il restra comme cela à less que nous ne «l'expulsions» ou «l'arrêtons». Je vais faire ce dernier:
$ unset BC_ENV_ARGS $ echo "$BC_ENV_ARGS" $ bc -q file2 42 27 $
Un autre exemple, impliquant la reproduction d'un autre shell:
Tapez les commands suivantes les unes après les autres dans votre shell et considérez les résultats. Voyez si vous pouvez prédire les résultats avant de les exécuter.
# fruit is not set echo "$fruit" sh -c 'echo "$fruit"' # fruit is set as a shell variable in the current shell only fruit=apple echo "$fruit" sh -c 'echo "$fruit"' sh -c "echo $fruit" ### NOT advised for use in scripts, for illustration only # fruit is exported, so it's accessible in current AND new processes export fruit echo "$fruit" sh -c 'echo "$fruit"' echo '$fruit' ### I threw this in to make sure you're not confused on quoting # fruit is unset again unset fruit echo "$fruit" sh -c 'echo "$fruit"' # setting fruit directly in environment of single command but NOT in current shell fruit=apple sh -c 'echo "$fruit"' echo "$fruit" fruit=apple echo "$fruit" # showing current shell is unaffected by directly setting env of single command fruit=cherry echo "$fruit" fruit=apricot sh -c 'echo "$fruit"' echo "$fruit" sh -c 'echo "$fruit"'
Et un dernier pour plus de difficulté: Pouvez-vous prédire la sortie des commands suivantes exécutées en séquence? 🙂
fruit=banana fruit=orange sh -c 'fruit=lemon echo "$fruit"; echo "$fruit"; export fruit=peach' echo "$fruit"
Veuillez mentionner dans les commentaires toutes les clarifications souhaitées; Je suis sûr que cela pourrait en utiliser quelques-uns. Mais cela devrait nous aider, même si c'est le cas.
Le problème est que le shell actuel étend la variable trop tôt; il n'est pas mis dans son context donc la command echo
ne reçoit aucun argument, c'est à dire que les commands finissent par être:
$ fruit=apple echo
Voici une solution de contournement où la variable ne se développe pas trop tôt en raison des guillemets simples:
$ fruit=apple sh -c 'echo $fruit'
Vous pouvez également utiliser un script shell d'une ligne qui montre que la variable fruit
est correctement transmise à la command exécutée:
$ cat /tmp/echof echo $fruit $ /tmp/echof $ fruit=apple /tmp/echof apple $ echo $fruit $
Quelques commentaires comme cette question a suscité des controverses et des discussions inattendues:
fruit
variable soit déjà exporté ou non n'affecte pas le comportement, ce qui count, c'est ce que la valeur de la variable est au moment précis où le shell l'étend. $ fruit d'export = banane $ fruit = pomme echo $ fruit banane
echo
soit embeddede n'affecte pas le problème OP. Cependant, il existe des cas où l'utilisation de fonctions internes ou shell avec cette syntaxe a des effets secondaires inattendus, par exemple: $ fruit d'export = banane $ fruit = pomme eval 'echo $ fruit' Pomme $ echo $ fruit Pomme
Bien qu'il y ait une similitude entre la question posée ici et celle-là, ce n'est pas exactement le même problème. Avec cette autre question, la valeur temporaire de la variable IFS
n'est pas encore disponible lorsque le mot shell sépare une autre variable $var
alors que la valeur temporaire de fruit
variable fruit
n'est pas encore disponible lorsque le shell développe la même variable.
Il y a aussi cette autre question où le PO pose la question de la signification de la syntaxe utilisée et plus précisément se request «pourquoi cela fonctionne-t-il? Ici, le PO est conscient de la signification mais signale un comportement inattendu et s'interroge sur sa cause, à savoir «pourquoi cela ne marche-t-il pas? Ok, après avoir lu de plus près la mauvaise capture d'écran postée sur l'autre question, la même situation y est en effet décrite ( BAZ=jake echo $BAZ
) alors oui, après tout c'est un doublon
Parce que les expansions sur la command line ont lieu avant les assignations de variables. La norme le dit ainsi :
Lorsqu'une command simple donnée doit être exécutée, les opérations suivantes doivent être exécutées:
Les mots qui sont reconnus comme des affectations variables […] sont sauvegardés pour le traitement aux étapes 3 et 4.
Les mots qui ne sont pas des assignations variables ou des redirections doivent être développés. […]
Les redirections doivent être effectuées comme décrit dans Redirection.
Chaque assignation de variable doit être étendue […] avant d'assigner la valeur.
Notez l'ordre: dans la première étape, les affectations ne sont sauvegardées que , les autres mots sont étendus, et à la fin seulement les affectations de variables ont lieu.
Bien sûr, il est très probable que la norme le dit seulement parce que ça a toujours été comme ça, et ils ont simplement codifié le comportement existant. Il ne dit rien sur l'histoire ou le raisonnement derrière. Le shell doit identifier les mots qui ressemblent à des affectations à un certain point (pour ne pas les prendre dans la command), donc je suppose qu'il pourrait fonctionner dans l'autre ordre, assigner les variables d'abord, puis étendre n'importe quoi sur la command line . (ou juste le faire de gauche à droite …)