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
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 appelleexport
). - 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 utiliserexport
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.
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 ditexport FOO=bar
, le processus enfant (et ses descendants) y aurait accès. Lun deux pourrait à son tour appelerexport FOO=buzz
pour changer la valeur de ses descendants, ou simplementFOO=buzz
pour changer la valeur uniquement pour lui-même . Est-ce à peu près correct?