Exporter une variable env pour qu'elle soit disponible dans tous les sous-shells, et peut-elle être modifiée?

22

Supposons que j'ai

export MY_VAR=0

dans ~/.bashrc.

J'ai un terminal gnome ouvert, et dans ce terminal, je change la $MY_VARvaleur en 200. Donc, si je le fais

echo $MY_VAR

dans ce terminal, 200est représenté.

Maintenant, j'ai ouvert un autre onglet dans mon terminal gnome, et fais

echo $MY_VAR

... et au lieu de 200, je l'ai 0.

Que dois-je faire pour conserver la valeur 200 lorsqu'un terminal modifie une variable d'environnement, rendant cette modification (mise à 200) disponible pour tous les sous-shells suivants et autres? Est-ce possible?

Quelqu'un vous utilise toujours MS-DOS
la source

Réponses:

23

Une copie de l'environnement se propage aux sous-shells, donc cela fonctionne:

$ export MY_VAR=200
$ bash
$ echo $MY_VAR
200

mais comme il s'agit d'une copie, vous ne pouvez pas obtenir cette valeur jusqu'au shell parent - pas en changeant l'environnement, au moins.

Il semble que vous vouliez réellement aller plus loin, c'est-à-dire créer quelque chose qui agit comme une variable globale, partagée par des shells "frères" initiés séparément du parent - comme votre nouvel onglet dans Gnome Terminal.

Généralement, la réponse est "vous ne pouvez pas, car les variables d'environnement ne fonctionnent pas de cette façon". Cependant, il y a une autre réponse, qui est, eh bien, vous pouvez toujours pirater quelque chose. Une approche consisterait à écrire la valeur de la variable dans un fichier, comme ~/.myvar, puis à l'inclure dans ~/.bashrc. Ensuite, chaque nouveau shell commencera avec la valeur lue dans ce fichier.

Vous pourriez aller plus loin - faites ~/.myvarêtre au format MYVAR=200, puis définissez PROMPT_COMMAND=source ~/.myvar, ce qui entraînerait la relecture de la valeur chaque fois que vous obtenez une nouvelle invite. Ce n'est pas encore tout à fait une variable globale partagée, mais ça commence à agir comme ça. Il ne s'activera pas jusqu'à ce qu'une invite revienne, ce qui, selon ce que vous essayez de faire, pourrait être une sérieuse limitation.

Et puis, bien sûr, la prochaine chose est d' écrire automatiquement les modifications ~/.myvar. Cela devient un peu plus compliqué, et je vais m'arrêter ici, car vraiment, les variables d'environnement n'étaient pas censées être un mécanisme de communication inter-shell, et il vaut mieux trouver simplement une autre façon de le faire.

mattdm
la source
10
J'entends le coquillage grincer sous la torture
Ouais désolé. Voilà pourquoi je me suis arrêté. :)
mattdm
"qui est de faire quelque chose qui agit comme une variable globale" - Eh bien, en effet, c'est ce que j'essayais d'accomplir. Le ~ / .myvar n'est pas joli, mais ça marche. Avez-vous une autre suggestion?
Quelqu'un vous utilise toujours MS-DOS le
2
Eh bien, reculons un peu. Pourquoi voulez-vous une variable globale?
mattdm
1
@mattdm: J'ai des "bureaucraties" auxquelles je dois obéir. L'un d'eux est un tas de codes que je dois engager avec un commentaire. Ces codes sont, maintenant, dans une hiérarchie de dossiers (home / utilisateur / projets / projectcode / code1 / code2). J'ai créé une fonction qui extrait le code1 et le code2 dans des vars env (donc quand j'ai besoin de faire une activité, j'appelle simplement la fonction dans le répertoire avec les codes), et eux, lors de la validation, j'utilise vim et il y a des fonctions qui lire ces vars et mettre automatiquement un commentaire. Je pense que l'utilisation de fichiers pour cela peut être une solution.
Quelqu'un vous utilise toujours MS-DOS le
13

Supposons que j'ai export MY_VAR=0dans ~/.bashrc.

Voilà votre erreur. Vous devez définir vos variables d'environnement dans ~/.profile, qui sont lues lorsque vous vous connectez. ~/.bashrcEst lu chaque fois que vous démarrez un shell; lorsque vous démarrez le shell interne, il remplace MY_VAR. Si vous ne l'aviez pas fait, votre variable d'environnement se propagerait vers le bas.

Pour plus d'informations sur ~/.bashrcvs ~/.profile, consultez mes précédents articles sur ce sujet .

Notez que la propagation vers le haut (obtenir une valeur modifiée du sous-shell automatiquement reflété dans le shell parent) n'est pas possible, point final.

Gilles 'SO- arrête d'être méchant'
la source
3

N'utilisez pas du tout de variables d'environnement. Utilisez des fichiers.

Pour empêcher les processus de marcher les uns sur les autres lors de la mise à jour / lecture du fichier, utilisez des fichiers de verrouillage et de petits scripts de mise à jour frontaux dont le but est simplement de mettre à jour un fichier avec $ 1 s'il n'est pas verrouillé. Les fichiers de verrouillage sont implémentés en vérifiant essentiellement si un fichier spécifique existe (/var/run/yourscript.lck) et si c'est le cas, attendez que le fichier disparaisse pendant un certain temps et échouez s'il ne le fait pas. Vous devez également supprimer le fichier de verrouillage une fois la mise à jour du fichier terminée.

Soyez prêt à gérer une situation où un script ne peut pas mettre à jour un fichier car le fichier est occupé.

LawrenceC
la source
8
Une astuce: utilisez des répertoires au lieu de fichiers pour le verrouillage, car mkdirfonctionne comme un test et création atomique.
mattdm
1
Si vous voulez parler de verrouillage, vous pourriez tout aussi bien utiliserflock(2)
Ehtesh Choudhury
@mattdm J'ai trouvé qu'un lien symbolique ln -s dummy lockfile(même s'il était cassé) peut également fonctionner car il échouera si le lien symbolique existe déjà. Je me demande un moyen de tester si c'est vraiment 100% sécurisé.
Aquarius Power
1

Quand je viens de googler cette question, j'essayais de résoudre le problème de la mise à jour de l'état (c'est-à-dire des variables de shell) à partir de sous-coquilles. Pour que l'on puisse affecter des variables, disons, à l'intérieur d'un pipeline - et l'affectation serait visible de manière transparente pour le parent.

Bien sûr, cela n'est pas possible d'une manière simple car les parties individuelles d'un pipeline sont exécutées dans des sous- forkcoquilles , qui sont éditées à partir du shell parent et ont donc une mémoire qui est une vue de copie sur écriture dans la mémoire du parent. Cependant, je supposais qu'une solution mince et transparente pourrait être possible en se basant sur des IPC de type " mémoire partagée " .

Et j'ai même trouvé une implémentation de cette conception exactement ... mais c'est en Perl.

Ajouter cette réponse de toute façon comme solution possible.

ulidtko
la source