Quelles étendues les variables shell peuvent-elles avoir?

Je viens de rencontrer un problème qui me montre que je « ne suis pas clair sur la portée des variables shell.

Jessayais de utilisez bundle install, qui est une commande Ruby qui utilise la valeur de $GEM_HOME pour faire son travail. Javais défini $GEM_HOME, mais la commande a ignoré cette valeur jusquà ce que jutilise export, comme dans export GEM_HOME=/some/path.

Jai lu que cela rend la variable « globale » (également connue sous le nom de variable denvironnement ), mais je ne « t comprenez ce que cela signifie. Je connais les globaux en programmation, mais pas dans différents programmes.

De plus, étant donné que mon paramétrage de telles variables ne sapplique quà la session shell actuelle, comment les définirais-je pour, par exemple, un processus démonisé?

Quelles étendues les variables shell peuvent-elles avoir?

Réponse

Les processus sont organisés sous forme darborescence: chaque processus a un parent unique, à lexception de init qui PID vaut toujours 1 et na pas de parent.

La création dun nouveau processus passe généralement par une paire de fork / execv appels système, où lenvironnement du processus enfant est une copie du processus parent.

Pour mettre une variable dans lenvironnement depuis le shell, vous devez export cette variable, afin quelle soit visible de manière récursive pour tous les enfants. Mais sachez que si un enfant change la valeur dune variable, la valeur modifiée nest visible que pour lui et tous les processus créés après ce changement (étant une copie , comme précédemment dit).

Tenez également compte du fait quun processus enfant pourrait changer son environnement, par exemple pourrait le réinitialiser aux valeurs par défaut, comme cela se fait probablement à partir de login exemple.

Commentaires

  • Ah! OK, laissez ‘ voir si je comprends cela. Dans le shell, si je dis FOO=bar, cela définit la valeur du processus actuel du shell. Si jexécute ensuite un programme comme (bundle install), cela crée un processus enfant, qui ne ‘ accéder à FOO. Mais si javais dit export FOO=bar, le processus enfant (et ses descendants) y aurait accès. Lun deux pourrait à son tour appeler export FOO=buzz pour changer la valeur de ses descendants, ou simplement FOO=buzz pour changer la valeur uniquement pour lui-même . Est-ce à peu près correct?
  • @NathanLong Que ‘ nest pas exactement ça: dans tous les shells modernes, une variable est soit exportée (et donc tout changement de valeur est reflétée dans lenvironnement des descendants) ou non exportée (ce qui signifie que la variable nest pas dans lenvironnement). En particulier, si la variable est déjà dans lenvironnement au démarrage du shell, elle est exportée.
  • Jétais un peu confus par la phrase  » si un enfant changer la valeur dune variable, la valeur modifiée nest visible que pour elle et tous les processus créés après cette modification « . Il serait plus correct de dire  » … visible pour lui et tous ses processus descendants créés après ce changement  » – lautre les enfants du processus parent, même ceux démarrés après le processus enfant, ne sont pas affectés.

Réponse

À au moins sous ksh et bash, les variables peuvent avoir trois portées, pas deux comme toutes les autres réponses le disent actuellement.

In en plus de la variable exportée (cest-à-dire denvironnement) et des portées des variables shell non exportées, il existe également une troisième plus étroite pour les variables locales de fonction.

Variables déclarées dans les fonctions shell avec le typeset token ne sont visibles que dans les fonctions dans lesquelles ils sont déclarés et dans les (sous) fonctions appelées à partir de là.

Ce ksh / bash code:

# Create a shell script named /tmp/show that displays the scoped variables values. echo "echo [$environment] [$shell] [$local]" > /tmp/show chmod +x /tmp/show # Function local variable declaration function f { typeset local=three echo "in function": . /tmp/show } # Global variable declaration export environment=one # Unexported (i.e. local) variable declaration shell=two # Call the function that creates a function local variable and # display all three variable values from inside the function f # Display the three values from outside the function echo "in shell": . /tmp/show # Display the same values from a subshell echo "in subshell": /tmp/show # Display the same values from a disconnected shell (simulated here by a clean environment start) echo "in other shell" env -i /tmp/show 

produit cette sortie:

in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] [] 

Comme vous pouvez le voir, la variable exportée est affichée à partir des trois premiers emplacements, les variables non exportées ne sont pas affichées en dehors du shell actuel et la variable locale de la fonction na aucune valeur en dehors de la fonction elle-même. Le dernier test ne montre aucune valeur, cest parce que les variables exportées ne sont pas partagées entre les shells, cest-à-dire quelles ne peuvent être héritées et que la valeur héritée ne peut pas être affectée par la suite par le shell parent.

Notez que ce dernier comportement est assez différent de celui de Windows où vous pouvez utiliser des variables système qui sont entièrement globales et partagées par tous les processus.

Réponse

Elles sont étendues par processus

Les autres répondants mont aidé à comprendre que la portée de la variable shell concerne les processus et leurs descendants .

Lorsque vous tapez une commande comme ls sur la ligne de commande, vous « êtes en fait forking un processus pour exécuter le programme ls. Le nouveau processus a votre shell comme parent.

Tout processus peut avoir ses propres variables « locales », qui sont pas transmis aux processus enfants. Il peut également définir des variables « denvironnement », qui le sont. Lutilisation de export crée une variable denvironnement. Dans lun ou lautre cas, les processus non liés (pairs de loriginal) ne verront pas la variable; nous contrôlons seulement quel proc enfant esses voir.

Supposons que vous ayez un shell bash, que nous « appellerons A. Vous tapez bash , qui crée un shell bash de processus enfant, que nous appellerons B. Tout ce que vous avez appelé export dans A sera toujours défini dans B.

Maintenant, en B, vous dites FOO=b. Une des deux choses se produira:

  • Si B na pas reçu (de A) une variable denvironnement appelée FOO, il créera une variable locale. Les enfants de B ne lobtiendront pas (à moins que B appelle export).
  • Si B a fait recevra (de A) une variable denvironnement appelée FOO, il la modifiera pour lui-même et ses enfants ensuite fourchus . Les enfants de B verront la valeur attribuée par B. Cependant, cela naffectera pas du tout A.

Voici « une démo rapide .

FOO=a # set "local" environment variable echo $FOO # "a" bash # forks a child process for the new shell echo $FOO # not set exit # return to original shell echo $FOO # still "a" export FOO # make FOO an environment variable bash # fork a new "child" shell echo $FOO # outputs "a" FOO=b # modifies environment (not local) variable bash # fork "grandchild" shell echo $FOO # outputs "b" exit # back to child shell exit # back to original shell echo $FOO # outputs "a" 

Tout cela explique mon problème initial: jai mis GEM_HOME dans mon shell, mais quand jai appelé bundle install, qui a créé un processus enfant. Comme je navais pas utilisé export, le processus enfant na pas reçu le shell « s GEM_HOME.

Dés-exportation

Vous pouvez « désexporter » une variable – lempêcher dêtre transmise aux enfants – en utilisant export -n FOO.

export FOO=a # Set environment variable bash # fork a shell echo $FOO # outputs "a" export -n FOO # remove environment var for children bash # fork a shell echo $FOO # Not set exit # back up a level echo $FOO # outputs "a" - still a local variable 

Commentaires

  • Quand vous dites  » il le modifiera pour lui-même et ses enfants  » vous devez préciser que seuls les enfants créés après la modification verra la valeur modifiée.
  • @enzotib – bon point. Mise à jour.

Réponse

La meilleure explication que je puisse trouver sur lexportation est celle-ci:

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

La variable définie dans un sous-shell ou un shell enfant nest visible que par le sous-shell dans lequel il est défini. La variable exportée est en fait une variable denvironnement. Donc, pour être clair, votre bundle install exécute son propre shell qui ne voit pas le $GEM_HOME à moins quil ne soit transformé en environment variable aka exportée.

Vous pouvez consulter la documentation de la portée des variables ici:

http://www.tldp.org/LDP/abs/html/subshells.html

Commentaires

  • Ah, jai donc eu tort dutiliser le terme  » variable denvironnement  » pour FOO=bar; vous devez utiliser export pour en faire une. Question corrigée en conséquence.
  • Jetez un œil au lien que jai ajouté.

Réponse

Il existe une hiérarchie de portées de variables, comme prévu.

Environnement

La portée la plus externe est lenvironnement. Cest la seule portée géré par le système dexploitation et est donc garanti pour chaque processus. Lorsquun processus est lancé, il reçoit un copie de l environnement de son parent après lequel les deux deviennent indépendants: la modification de l environnement de l enfant ne change pas celui de son parent, et la modification de l environnement du parent ne change pas celui d un enfant déjà existant. h2> Variables shell

Les shells ont leur propre notion de variables. Cest là que les choses commencent à devenir un peu confuses.

Lorsque vous attribuez une valeur à une variable dans un shell, et que cette variable existe déjà dans lenvironnement, la variable denvironnement reçoit la nouvelle valeur. Cependant, si la variable nest pas encore dans lenvironnement, elle devient une variable shell . Les variables Shell nexistent que dans le processus shell, de la même manière que les variables Ruby nexistent que dans un script Ruby. Ils ne sont jamais hérités par les processus enfants.

Cest ici que le mot-clé export entre en jeu. Il copie une variable shell dans lenvironnement du processus shell permettant aux processus enfants den hériter.

Variables locales

Les variables locales sont des variables shell étendues aux blocs de code les contenant. Vous déclarez des variables locales avec le mot clé typeset (portable) ou local ou declare (Bash ). Comme les autres variables shell, les variables locales ne sont pas héritées par les processus enfants. Les variables locales ne peuvent pas non plus être exportées.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *