linux - sous - tcsh vs bash




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

J'essaie d'écrire un script shell qui, lorsqu'il est lancé, 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, et cela peut entraîner la définition de l'environnement "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?


Ajoutez le drapeau -l en haut de votre script bash

#!/usr/bin/env bash -l

...

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

Les valeurs avec NAME1 et NAME2 ont maintenant été exportées vers votre environnement actuel, mais ces modifications ne sont pas permanentes. Si vous voulez qu'ils soient permanents, vous devez les ajouter à votre fichier .bashrc ou à un 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).

Autres que les conditions écrites en fonction de $ SHELL / $ TERM, non. Quel est le problème avec l'utilisation de Perl? C'est assez omniprésent (je ne peux pas penser à une seule variante UNIX qui ne l'a pas), et ça vous évitera le problème.


Cela fonctionne - ce n'est pas ce que j'utiliserais, mais ça '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 shell Korn, exportera la variable d'environnement, puis se remplacera par un nouveau shell interactif.

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

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

Lorsque le script est exécuté, vous êtes dans un nouveau shell, un autre C shell 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 vous utilisez 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 Control-D ), votre shell se ferme, vous déconnectant probablement de cette fenêtre ou vous ramenant au niveau précédent du shell à partir duquel les expériences ont démarré.

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 l'option -i pour créer un shell interactif). Vous pouvez aussi ajouter "[email protected]" après l'option de relayer tous les autres arguments, ce qui pourrait rendre le shell utilisable comme un outil général '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 bit "${@-'-i'}" signifie que si la liste d'arguments contient au moins un argument, utilisez la liste d'arguments d'origine; sinon, substituez -i pour les arguments inexistants '.


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 attendu et voulu.


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

Ensuite, le script que vous allez rechercher dans l'un des deux shells a une commande avec cette dernière forme, qui correspond à un alias approprié dans chaque shell.

Si je trouve les alias concrets, je les posterai.


Je ne vois aucune réponse documentant comment contourner ce problème avec des processus de coopération. Un modèle courant avec des choses comme ssh-agent est que le processus fils imprime 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 le démarrage de l'agent, mais ne vous permet pas de l'utiliser, sauf si vous copiez-collez cette sortie dans l'invite du shell.)

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 constater, csh et tcsh utilisent setenv pour définir des variables.)

Votre propre programme peut le faire aussi.

bash$ foo=$(makefoo)

Votre script makefoo ne ferait que calculer et imprimer la valeur, et laisser l'appelant faire ce qu'il veut avec - l'assigner à une variable est un cas d'utilisation courant, mais probablement pas quelque chose que vous voulez coder en dur dans l'outil qui produit le valeur.


Sous OS X bash, vous pouvez effectuer les opérations suivantes:
Créez le fichier de 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 tant que vous avez le dossier contenant votre fichier script ajouté au chemin.


Techniquement, c'est correct - seul 'eval' ne fourche 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 l'environnement (modifié) est transmis à tous les processus descendants.

Ipso facto, la variable d'environnement modifiée 'sticks' - tant que vous utilisez le programme / shell parent.

S'il est absolument nécessaire que la variable d'environnement reste après la sortie du parent (Perl ou shell), il est nécessaire que le shell parent fasse le gros du travail. Une méthode que j'ai vu dans la documentation est que le script actuel génère un fichier exécutable avec le langage 'export' nécessaire, puis tromper le shell parent en l'exécutant - en étant toujours conscient du fait que vous devez commande avec 'source' si vous essayez de laisser derrière vous une version non volatile de l'environnement modifié. Un Kluge au mieux.

La deuxième 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 arrêtez le script d'initialisation, il peut rendre votre shell indisponible la prochaine fois qu'il tentera de se lancer. Il y a beaucoup d'outils pour modifier le shell actuel; En apposant les réglages nécessaires au «lanceur», vous faites également avancer ces changements. Généralement pas une bonne idée; Si vous avez seulement besoin des changements d'environnement pour une suite d'applications particulière, vous devrez revenir en arrière et retourner le script de lancement du shell à son état vierge (en utilisant vi ou quoi que ce soit) par la suite.

En bref, il n'y a pas de bonnes (et faciles) méthodes. Vraisemblablement, cela a été rendu difficile de s'assurer que la sécurité du système n'était pas irrévocablement compromise.


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 d'environnement TESTCASE, et ensuite avoir une autre commande qui l'utiliserait pour exécuter le test. Inutile de dire que j'ai eu le même problème que vous.

Mais ensuite je suis venu avec ce hack simple:

Première commande (ensemble de 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

Utilisez la syntaxe d'appel "script espace". 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 ./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 la source set_env_vars.sh , mais il est plus court à taper et peut fonctionner dans certains endroits où la source ne le fait pas.



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 héritent eux-mêmes des copies.

Une chose que vous pouvez faire est d'écrire un script qui émet les commandes correctes pour tcsh ou sh en fonction de son invocation. 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 "in" et "do".

C'est à peu près la façon dont les paramètres du terminal de connexion étaient utilisés: un script produirait des statuts à 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.


Vous pouvez en appeler un autre avec les différents bash_profile. En outre, vous pouvez créer un fichier bash_profile spécial à utiliser dans un environnement multi-bashprofile.

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


Vous pouvez toujours utiliser des alias

alias your_env='source ~/scripts/your_env.sh'




tcsh