Un script shell peut-il définir les variables d'environnement du shell appelant?

j'essaie d'écrire un script shell qui, une fois lancé, définira des variables d'environnement qui resteront définies dans l'interpréteur de commandes de l'appelant.

setenv FOO foo

dans csh/ tcsh, ou

export FOO=foo

dans sh/bash ne le met que pendant l'exécution du script.

je sais déjà que

source myscript

exécutera les commandes du script plutôt que de lancer un nouvel interpréteur de commandes, ce qui peut avoir pour résultat "l'appelant" environnement.

Mais voici le hic:

je veux que ce script soit callable depuis bash ou csh. En d'autres termes, je veux que les utilisateurs de l'un ou l'autre shell puissent exécuter mon script et faire modifier l'environnement de leur shell. Donc 'source' ne fonctionnera pas pour moi, puisqu'un utilisateur qui exécute csh ne peut pas source un script bash, et un utilisateur qui exécute bash ne peut pas source un script csh.

y a-t-il une solution raisonnable qui n'implique pas d'avoir pour écrire et maintenir deux versions sur le script?

336
demandé sur codeforester 2009-01-30 21:50:22

21 réponses

votre processus shell a une copie de l'environnement du parent et aucun accès à l'environnement du processus parent. Lorsque votre processus shell se termine, tous les changements que vous avez apportés à son environnement sont perdus. Sourcing un fichier de script est la méthode la plus couramment utilisée pour configurer un environnement shell, vous pouvez juste vouloir mordre la balle et maintenir un pour chacune des deux saveurs de shell.

218
répondu converter42 2014-06-05 14:43:25

utilisez la syntaxe d'appel" point Space script". Par exemple, voici comment le faire en utilisant le chemin complet vers un script:

. /path/to/set_env_vars.sh

Et voici comment le faire si vous êtes dans le même répertoire que le script:

. set_env_vars.sh

ceux-ci exécutent le script sous le shell courant au lieu d'en charger un autre (ce qui se produirait si vous faisiez ./set_env_vars.sh ). Parce qu'il fonctionne dans le même shell, les variables d'environnement vous sera disponible quand il sort.

c'est la même chose que d'appeler source set_env_vars.sh , mais c'est plus court pour taper et pourrait fonctionner dans certains endroits où source ne fonctionne pas.

209
répondu Humberto Romero 2017-10-17 12:41:29

vous n'allez pas pouvoir modifier le shell de l'appelant parce que c'est dans un contexte de processus différent. Quand les processus enfant héritent les variables de votre shell, ils sont hériter des copies elles-mêmes.

Une chose que vous pouvez faire est d'écrire un script qui émet les commandes appropriées pour tcsh ou sh basé sur la façon dont il est invoqué. Si vous êtes script est "setit" puis faire:

ln -s setit setit-sh

et

ln -s setit setit-csh

maintenant soit directement soit dans un alias, vous faites ceci de sh

eval `setit-sh`

ou ce de csh

eval `setit-csh`

setit utilise 0 $ pour déterminer son style de sortie.

cela rappelle la façon dont les gens utilisent pour obtenir le terme environnement variable ensemble.

L'avantage ici est que setit est juste écrit dans n'importe quel shell comme dans:

#!/bin/bash
arg0="151940920"
arg0=${arg0##*/}
for nv in \
   NAME1=VALUE1 \
   NAME2=VALUE2
do
   if [ x$arg0 = xsetit-sh ]; then
      echo 'export '$nv' ;'
   elif [ x$arg0 = xsetit-csh ]; then
      echo 'setenv '${nv%%=*}' '${nv##*=}' ;'
   fi
done

avec les liens symboliques donnés ci-dessus, et l'éval expression en backquoted, cela a le résultat désiré.

pour simplifier l'invocation pour CSH, tcsh, ou coquilles similaires:

alias dosetit 'eval `setit-csh`'

ou pour sh, bash, et similaires:

alias dosetit='eval `setit-sh`'

une bonne chose à ce sujet est que vous avez seulement à maintenir la liste en un seul endroit. En théorie, vous pouvez même coller la liste dans un fichier et mettre cat nvpairfilename entre "in" et "do".

C'est à peu près la façon dont le shell de login les réglages du terminal étaient faits: un script produirait des États à exécuter dans le shell de connexion. Un alias serait généralement utilisé pour rendre l'invocation simple, comme dans "tset vt100". Comme mentionné dans une autre réponse, il existe également des fonctionnalités similaires dans le serveur de nouvelles INN UseNet.

52
répondu Thomas Kammeyer 2015-05-20 14:51:54

Dans mon .bash_profile j'ai :

# No Proxy
function noproxy
{
    /usr/local/sbin/noproxy  #turn off proxy server
    unset http_proxy HTTP_PROXY https_proxy HTTPs_PROXY
}


# Proxy
function setproxy
{
    sh /usr/local/sbin/proxyon  #turn on proxy server 
    http_proxy=http://127.0.0.1:8118/
    HTTP_PROXY=$http_proxy
    https_proxy=$http_proxy
    HTTPS_PROXY=$https_proxy
    export http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
}

donc quand je veux désactiver le mandataire, la fonction(s) à exécuter dans le shell de connexion et définit les variables comme prévu et voulu.

44
répondu chris 2016-09-17 16:22:33

c'est" en quelque sorte "possible grâce à l'utilisation de gdb et setenv(3) , bien que j'ai du mal à recommander réellement de faire cela. (De plus, c'est-à-dire que l'ubuntu le plus récent ne vous laissera pas réellement faire cela sans dire au noyau d'être plus permissif sur ptrace, et la même chose peut aller pour d'autres distros aussi bien).

$ cat setfoo
#! /bin/bash

gdb /proc/${PPID}/exe ${PPID} <<END >/dev/null
call setenv("foo", "bar", 0)
END
$ echo $foo

$ ./setfoo
$ echo $foo
bar
23
répondu Kjetil Joergensen 2011-07-08 22:03:34

cela fonctionne - il ne est pas ce que je voudrais utiliser, mais il 'fonctionne'. Créons un script teredo pour définir la variable d'environnement TEREDO_WORMS :

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL -i

il sera interprété par le Korn shell, exporte la variable d'environnement, puis se remplace par un nouveau shell interactif.

Avant d'exécuter ce script, nous avons SHELL dans l'environnement du shell C, et la variable d'environnement TEREDO_WORMS n'est pas défini:

% env | grep SHELL
SHELL=/bin/csh
% env | grep TEREDO
%

lorsque le script est lancé, vous êtes dans un nouveau shell, un autre shell C interactif, mais la variable d'environnement est définie:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

lorsque vous sortez de ce shell, le shell d'origine prend la relève:

% exit
% env | grep TEREDO
%

la variable d'environnement n'est pas définie dans l'environnement original de shell. Si vous utilisez exec teredo pour exécuter la commande, le shell interactif est remplacé par le Korn shell définit l'environnement, puis celui-ci est remplacé à son tour par un nouveau shell C interactif:

% exec teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

si vous tapez exit (ou Control-D ), alors votre shell sort, probablement en vous déconnectant de cette fenêtre, ou en vous ramenant au niveau précédent de shell d'où les expériences ont commencé.

le même mécanisme fonctionne pour la coque Bash ou Korn. Vous pouvez trouver que le prompt après les commandes exit apparaît dans de drôles d'endroits.


noter la discussion dans les commentaires. Ce n'est pas une solution que je recommanderais, mais elle atteint l'objectif déclaré d'un script unique pour définir l'environnement qui fonctionne avec tous les shells (qui acceptent l'option -i pour faire un shell interactif). Vous pouvez également ajouter "$@" après l'option de relais d'autres arguments, ce qui pourrait alors rendre le shell utilisable comme environnement de jeu général et exécuter commande de l'outil. Vous pourriez vouloir omettre le -i s'il y a d'autres arguments, conduisant à:

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL "${@-'-i'}"

le bit "${@-'-i'}" signifie "Si la liste d'arguments contient au moins un argument, utiliser la liste d'arguments originale; sinon, remplacer -i par -i .

12
répondu Jonathan Leffler 2012-06-07 16:18:34

vous devez utiliser les modules, voir http://modules.sourceforge.net/

EDIT: le paquet de modules n'a pas été mis à jour depuis 2012 mais fonctionne toujours correctement pour les bases. Toutes les nouvelles fonctionnalités, cloches et sifflets se produisent dans lmod ce jour (que je l'aime plus): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

11
répondu Davide 2015-10-06 14:29:48

une autre solution que je ne vois pas mentionnée est d'écrire la valeur de la variable dans un fichier.

j'ai rencontré un problème très similaire où je voulais pouvoir exécuter le test du dernier set (au lieu de tous mes tests). Mon premier plan était d'écrire une commande pour configurer la variable env TESTCASE, puis d'avoir une autre commande qui utiliserait ceci pour exécuter le test. Inutile de dire que j'avais exactement le même problème que vous.

mais j'ai trouvé ce simple hack:

Première commande ( testset ):

#!/bin/bash

if [ $# -eq 1 ]
then
  echo  > ~/.TESTCASE
  echo "TESTCASE has been set to: "
else
  echo "Come again?"
fi

Second commandement ( testrun ):

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE
8
répondu dkinzer 2013-07-26 19:21:34

ajouter le drapeau-l en haut de votre script bash i.e.

#!/usr/bin/env bash -l

...

export NAME1="VALUE1"
export NAME2="VALUE2"

les valeurs avec NAME1 et NAME2 seront maintenant exportées dans votre environnement actuel, mais ces changements ne sont pas permanents. Si vous voulez être permanente, vous devez les ajouter à votre .bashrc fichier ou un autre fichier init.

De l'homme pages:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).
4
répondu cristobal 2014-06-16 12:43:43

vous pouvez demander au processus enfant d'imprimer ses variables d'environnement (en appelant" env"), puis faire une boucle sur les variables d'environnement imprimées dans le processus parent et appeler" exporter " sur ces variables.

le code suivant est basé sur Capturing output of find . - print0 dans un tableau bash

si le shell parent est le bash, vous pouvez utiliser

while IFS= read -r -d $'"151900920"' line; do
    export "$line"
done < <(bash -s <<< 'export VARNAME=something; env -0')
echo $VARNAME

si le shell parent est le tiret, alors read ne fournit pas le drapeau-d et le code devient plus compliqué

TMPDIR=$(mktemp -d)
mkfifo $TMPDIR/fifo
(bash -s << "EOF"
    export VARNAME=something
    while IFS= read -r -d $'"151910920"' line; do
        echo $(printf '%q' "$line")
    done < <(env -0)
EOF
) > $TMPDIR/fifo &
while read -r line; do export "$(eval echo $line)"; done < $TMPDIR/fifo
rm -r $TMPDIR
echo $VARNAME
3
répondu klaus se 2017-05-23 12:34:53

vous pouvez invoquer un autre Bash avec le différent bash_profile. De plus, vous pouvez créer un bash_profile spécial pour une utilisation dans un environnement multi-bashprofile.

rappelez-vous que vous pouvez utiliser fonctions à l'intérieur de bashprofile, et que les fonctions seront disponibles globalement. par exemple, "fonction user { export USER_NAME $1}" peut définir une variable dans runtime, par exemple: user olegchir & & env / grep olegchir

2
répondu Oleg Chirukhin 2010-10-29 06:15:02

techniquement, c'est correct -- seulement "eval" ne biffe pas un autre shell. Cependant, du point de vue de l'application que vous essayez d'exécuter dans l'environnement modifié, la différence est nulle: l'enfant hérite de l'environnement de son parent, de sorte que le (modifié) l'environnement est transmis à tous les processus descendant.

Ipso facto, la variable d'environnement modifiée 'sticks' -- aussi longtemps que vous exécutez sous le programme/shell parent.

S'il est absolument nécessaire que la variable d'environnement reste après que le parent (Perl ou shell) est sorti, il est nécessaire que le Parent shell fasse le levage lourd. Une méthode que j'ai vue dans la documentation est que le script courant lance un fichier exécutable avec le langage 'export' nécessaire, et ensuite fait en sorte que le shell parent l'exécute -- en étant toujours conscient du fait que vous devez préfacer la commande avec 'source' si vous essayez de quitter un version non volatile de l'environnement modifié derrière. Un Kluge au mieux.

la seconde méthode consiste à modifier le script qui initie l'environnement shell (.bashrc ou autre) pour contenir le paramètre modifié. Cela peut être dangereux -- si vous utilisez le script d'initialisation, il peut rendre votre shell indisponible la prochaine fois qu'il essaie de lancer. Il y a beaucoup d'outils pour modifier le shell actuel; en fixant les modifications nécessaires au "lanceur" vous effectivement pousser ces changements aussi. En général, ce n'est pas une bonne idée; si vous n'avez besoin que des changements d'environnement pour une suite d'application particulière, vous devrez revenir en arrière et retourner le script de lancement du shell à son état originel (en utilisant vi ou n'importe quoi d'autre) par la suite.

en bref, il n'y a pas de bonnes (et faciles) méthodes. Sans doute cela a été difficile d'assurer la sécurité du système n'a pas été irrévocablement compromis.

1
répondu David Lovering 2011-05-18 13:23:39

la réponse courte est non, vous ne pouvez pas modifier l'environnement du processus parent, mais il semble que ce que vous voulez est un environnement avec des variables d'environnement personnalisées et le shell que l'Utilisateur a choisi.

alors pourquoi pas quelque chose comme

#!/usr/bin/env bash
FOO=foo $SHELL

puis quand vous avez terminé avec l'environnement, juste exit .

1
répondu Andrew 2013-02-28 06:41:39

vous pouvez toujours utiliser des alias

alias your_env='source ~/scripts/your_env.sh'
1
répondu user1667208 2014-04-10 23:39:37

une autre option consiste à utiliser "les Modules D'environnement" ( http://modules.sourceforge.net / ). Cela introduit malheureusement une troisième langue dans le mélange. Vous définissez L'environnement avec le langage Tcl, mais il y a quelques commandes pratiques pour les modifications typiques (prepend vs. append vs set). Vous devrez également installer des modules d'environnement. Vous pouvez alors utiliser module load *XXX* pour nommer l'environnement que vous voulez. La commande module est essentiellement un pseudonyme de fantaisie pour le mécanisme eval décrit ci-dessus par Thomas Kammeyer. Le principal avantage ici est que vous pouvez maintenir l'environnement dans une langue et compter sur "les Modules D'environnement" pour le traduire en sh, ksh, bash, csh, tcsh, zsh, python (?!?!!), etc.

1
répondu Howard Hobbes 2014-10-23 20:02:45

Je l'ai fait il y a de nombreuses années. Si je me souviens bien, j'ai inclus un alias dans chacun .bashrc et .cshrc, avec paramètres, aliasing les formes respectives de réglage de l'environnement à une forme commune.

alors le script que vous allez source dans l'un des deux shells a une commande avec cette dernière forme, qui est aliasée appropriée dans chaque shell.

si je trouve les alias concrets, je les posterai.

1
répondu sancho.s 2015-10-30 15:14:45

j'ai créé une solution en utilisant des pipes, eval et signal.

parent() {
    if [ -z "$G_EVAL_FD" ]; then
            die 1 "Rode primeiro parent_setup no processo pai"
    fi
    if [ $(ppid) = "$$" ]; then
            "$@"
    else
            kill -SIGUSR1 $$
            echo "$@">&$G_EVAL_FD
    fi
}
parent_setup() {
    G_EVAL_FD=99
    tempfile=$(mktemp -u)
    mkfifo "$tempfile"
    eval "exec $G_EVAL_FD<>'$tempfile'"
    rm -f "$tempfile"
    trap "read CMD <&$G_EVAL_FD; eval \"$CMD\"" USR1
}
parent_setup #on parent shell context
( A=1 ); echo $A # prints nothing
( parent A=1 ); echo $A # prints 1

ça peut marcher avec n'importe quel ordre.

1
répondu Luiz Angelo Daros de Luca 2016-09-21 21:43:53

sous OS X bash vous pouvez faire ce qui suit:

Créer le fichier script bash pour désactiver la variable

#!/bin/bash
unset http_proxy

Rendre le fichier exécutable

sudo chmod 744 unsetvar

créer un alias

alias unsetvar='source /your/path/to/the/script/unsetvar'

il devrait être prêt à utiliser si longtemps que vous avez le dossier contenant votre fichier de script ajouté au chemin.

0
répondu Marton Tatai 2017-01-19 09:26:51

je ne vois pas de réponse à documenter la façon de contourner ce problème avec coopérant processus. Un modèle commun avec des choses comme ssh-agent est d'avoir le processus enfant imprimer une expression que le parent peut eval .

bash$ eval $(shh-agent)

par exemple, ssh-agent a des options pour sélectionner la syntaxe de sortie compatible csh ou Bourne.

bash$ ssh-agent
SSH2_AUTH_SOCK=/tmp/ssh-era/ssh2-10690-agent; export SSH2_AUTH_SOCK;
SSH2_AGENT_PID=10691; export SSH2_AGENT_PID;
echo Agent pid 10691;

(cela provoque l'agent de commencer à courir, mais ne vous permet pas de réellement utilisez-le, sauf si vous copiez-collez cette sortie à votre invite shell.) Comparer:

bash$ ssh-agent -c
setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent;
setenv SSH2_AGENT_PID 10752;
echo Agent pid 10752;

(comme vous pouvez le voir, csh et tcsh utilise setenv pour mettre varibles.)

Votre propre programme peut faire cela aussi.

bash$ foo=$(makefoo)

votre script makefoo calculerait et imprimerait simplement la valeur, et laisserait l'appelant faire ce qu'il veut avec elle -- l'assigner à une variable est un cas d'usage courant, mais probablement pas quelque chose que vous voulez coder en dur dans l'outil qui produit de la valeur.

0
répondu tripleee 2017-11-23 09:25:36

ce n'est pas ce que j'appellerais outstanding, mais cela fonctionne aussi si vous avez besoin d'appeler le script depuis le shell de toute façon. Ce n'est pas une bonne solution, mais pour une seule variable d'environnement statique, cela fonctionne assez bien.

1.) Créer un script avec une condition qui sort soit 0 (réussi) soit 1 (pas réussi)

if [[ $foo == "True" ]]; then
    exit 0
else
    exit 1

2.) Créer un alias dépendant du code de sortie.

alias='myscript.sh && export MyVariable'

vous appelez le alias, qui appelle le script, qui évalue la condition, qui est requis pour quitter zéro via le '& & ' afin de définir la variable d'environnement dans le shell parent.

c'est flotsam, mais il peut être utile dans un pincement.

0
répondu user1802263 2018-08-31 15:50:33

Autres que les écrits des conditionnelles en fonction de ce que $SHELL/$TERME est défini, non. Qu'y a-t-il de mal à utiliser Perl? C'est assez ubiquiste (Je ne peux pas penser à une seule variante UNIX qui ne l'a pas), et ça vous évitera les ennuis.

-9
répondu phresus 2009-01-30 19:08:12