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


J'essaie d'écrire un script shell qui, lorsqu'il est exécuté, va définir des variables d'environnement qui resteront définies dans le shell de l'appelant.

setenv FOO foo

dans csh / tcsh, ou

export FOO=foo

dans sh / bash seulement le définir 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 nouveau shell, ce qui peut entraîner la définition de l'environnement "de l'appelant".

Mais voici le frotter:

Je veux que ce script soit appelable depuis bash ou csh. En d'autres termes, je souhaite que les utilisateurs de l'un ou l'autre shell puissent exécuter mon script et que l'environnement de leur shell soit modifié. Donc, 'source' ne fonctionnera pas pour moi, car un utilisateur qui exécute csh ne peut pas générer un script bash, et un utilisateur qui exécute bash ne peut pas générer un script csh.

Existe-t-il une solution raisonnable qui n'implique pas d'écrire et de maintenir deux versions sur le script?


323
2018-01-30 18:50


origine


Réponses:


Votre processus shell possède une copie de l'environnement du parent et aucun accès à l'environnement du processus parent. Lorsque votre processus shell termine toutes les modifications que vous avez apportées à son environnement sont perdues. 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 en maintenir un pour chacune des deux saveurs de shell.


213
2018-01-30 19:06



Utilisez la syntaxe d'appel "script d'espace de points". Par exemple, voici comment le faire en utilisant le chemin complet d'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 actuel au lieu d'en charger un autre (ce qui arriverait si vous le faisiez ./set_env_vars.sh). Parce qu'il s'exécute dans le même shell, les variables d'environnement que vous définissez seront disponibles quand il sortira.

C'est la même chose que d'appeler source set_env_vars.sh, mais il est plus court à taper et pourrait fonctionner dans certains endroits où source ne le fait pas.


185
2018-02-12 23:04



Vous ne serez pas en mesure de modifier le shell de l'appelant car il se trouve dans un contexte de processus différent. Lorsque les processus enfants héritent des 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 correctes pour tcsh ou sh basé comment il est invoqué. Si votre script est "setit" alors faites:

ln -s setit setit-sh

et

ln -s setit setit-csh

Maintenant, soit directement ou dans un alias, vous le faites de sh

eval `setit-sh`

ou cela de csh

eval `setit-csh`

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

Cela rappelle l'utilisation de la variable d'environnement TERM par les utilisateurs.

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

#!/bin/bash
arg0=$0
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'évaluation de l'expression rétropolée, ceci a le résultat désiré.

Pour simplifier l'appel pour les shells csh, tcsh ou similaires:

alias dosetit 'eval `setit-csh`'

ou pour sh, bash, et autres:

alias dosetit='eval `setit-sh`'

Une bonne chose à ce sujet est que vous n'avez qu'à maintenir la liste à un endroit. En théorie, vous pouvez même coller la liste dans un fichier et mettre cat nvpairfilename entre "dans" et "faire".

Cela correspond à peu près à la configuration des paramètres du terminal de login: 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 news INN UseNet.


51
2018-01-30 19:06



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 proxy, la (les) fonction (s) s'exécute dans le shell de connexion et définit les variables comme prévu et voulu.


44
2017-11-19 23:46



C'est "assez" possible en utilisant gdb et setenv (3), même si j'ai du mal à le faire. (De plus, l'ubuntu le plus récent ne vous laissera pas faire cela sans dire au noyau d'être plus permissif à propos de ptrace, et il en ira de même pour les autres distributions).

$ cat setfoo
#! /bin/bash

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

$ ./setfoo
$ echo $foo
bar

21
2017-07-08 22:03



Cela fonctionne - ce n'est pas ce que j'utiliserais, mais ça 'fonctionne'. Créons un script teredo définir la variable d'environnement TEREDO_WORMS:

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

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

Avant d'exécuter ce script, nous avons SHELL définir dans l'environnement de la coquille 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 exécuté, 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 quittez ce shell, le shell d'origine prend le relais:

% exit
% env | grep TEREDO
%

La variable d'environnement n'est pas définie dans l'environnement du shell d'origine. Si tu utilises exec teredo Pour exécuter la commande, le shell interactif d'origine est remplacé par le shell Korn qui définit l'environnement, puis celui-ci est remplacé par un nouveau shell C interactif:

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

Si vous tapez exit (ou Contrôle-D), alors votre shell se ferme, vous déconnectant probablement de cette fenêtre, ou vous ramène au niveau de shell précédent à partir duquel les expériences ont commencé.

Le même mécanisme fonctionne pour Bash ou Korn shell. Vous pouvez constater que l'invite après les commandes de sortie apparaît dans des endroits amusants.


Notez 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 le -i option pour faire un shell interactif). Vous pouvez également ajouter "$@" après l'option de relayer tous les autres arguments, ce qui pourrait alors rendre le shell utilisable en tant qu'outil "set environment and execute command". Vous pourriez vouloir omettre le -i s'il y a d'autres arguments, menant à:

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

le "${@-'-i'}" bit signifie 'si la liste d'arguments contient au moins un argument, utilisez la liste d'arguments d'origine; sinon, substitut -i pour les arguments inexistants ».


12
2018-01-30 22:17



Vous devriez utiliser des modules, voir http://modules.sourceforge.net/

EDIT: Le paquet de modules n'a pas été mis à jour depuis 2012 mais fonctionne toujours bien pour les bases. Toutes les nouveautés, cloches et sifflets se produisent aujourd'hui dans lmod (ce qui me plait plus): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod


11
2018-02-04 18:52



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

Je suis tombé sur un problème très similaire où je voulais être en mesure d'exécuter le dernier test (au lieu de tous mes tests). Mon premier plan était d'écrire une commande pour définir la variable env TESTCASE, puis une autre commande qui l'utilisait pour exécuter le test. Inutile de dire que j'avais le même problème que vous.

Mais ensuite je suis venu avec ce hack simple:

Première commande ( testset ):

#!/bin/bash

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

Deuxième commande (testrun ):

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE

6
2017-07-26 19:21



Ajoutez l'indicateur -l en haut de votre script bash.

#!/usr/bin/env bash -l

...

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

Les valeurs avec NAME1 et NAME2 aura maintenant été exporté vers votre environnement actuel, mais ces changements ne sont pas permanents. Si vous voulez qu'ils soient permanents, vous devez les ajouter à votre .bashrc fichier ou autre fichier init.

À partir des pages de manuel:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).

4
2018-06-16 12:43