¿Puede un script de shell establecer variables de entorno del shell que llama?


Estoy tratando de escribir un script de shell que, cuando se ejecute, establecerá algunas variables de entorno que permanecerán establecidas en el shell de la persona que llama.

setenv FOO foo

En csh / tcsh, o

export FOO=foo

En sh/bash solo se establece durante la ejecución del script.

Ya sé que

source myscript

Ejecutará los comandos del script en lugar de lanzar un nuevo shell, y eso puede resultar en establecer el entorno del "llamador".

Pero aquí está el problema:

Quiero que este script sea se puede llamar desde bash o csh. En otras palabras, quiero que los usuarios de cualquiera de los shell puedan ejecutar mi script y cambiar el entorno de su shell. Así que 'source' no funcionará para mí, ya que un usuario que ejecuta csh no puede obtener un script bash, y un usuario que ejecuta bash no puede obtener un script csh.

¿Hay alguna solución razonable que no implique tener que escribir y mantener DOS versiones en el script?

Author: codeforester, 2009-01-30

21 answers

Su proceso shell tiene una copia del entorno del padre y no tiene acceso al entorno del proceso padre en absoluto. Cuando su proceso shell termina, cualquier cambio que haya realizado en su entorno se pierde. El aprovisionamiento de un archivo de script es el método más comúnmente utilizado para configurar un entorno de shell, es posible que solo desee morder la bala y mantener uno para cada uno de los dos sabores de shell.

 218
Author: converter42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-05 14:43:25

Utilice la sintaxis de llamada "dot space script". Por ejemplo, he aquí cómo hacerlo usando la ruta completa a un script:

. /path/to/set_env_vars.sh

Y aquí está cómo hacerlo si estás en el mismo directorio que el script:

. set_env_vars.sh

Estos ejecutan el script bajo el shell actual en lugar de cargar otro (que es lo que pasaría si lo hicieras ./set_env_vars.sh). Debido a que se ejecuta en el mismo shell, las variables ambientales que establezca estarán disponibles cuando salga.

Esto es lo mismo que llamando source set_env_vars.sh, pero es más corto para escribir y podría funcionar en algunos lugares donde source no lo hace.

 209
Author: Humberto Romero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-17 12:41:29

No vas a poder modificar el shell de la persona que llama porque está en un contexto de proceso diferente. Cuando los procesos hijos heredan las variables de tu shell, son heredando copias.

Una cosa que puede hacer es escribir un script que emita los comandos correctos para tcsh o sh basado en cómo se invoca. Si tu script es "setit" entonces haz:

ln -s setit setit-sh

Y

ln -s setit setit-csh

Ahora, ya sea directamente o en un alias, lo haces desde sh

eval `setit-sh`

O esto de csh

eval `setit-csh`

Setit usa $0 para determinar su estilo de salida.

Esto es reminiscente de cómo la gente usa para obtener el conjunto de variables de entorno de TÉRMINO.

La ventaja aquí es que setit está escrito en cualquier shell que desee como en:

#!/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

Con los enlaces simbólicos dados arriba, y la evaluación de la expresión backquoted, esto tiene el resultado deseado.

Para simplificar la invocación para csh, tcsh o shells similares:

alias dosetit 'eval `setit-csh`'

O para sh, bash, y el como:

alias dosetit='eval `setit-sh`'

Una cosa buena de esto es que solo tienes que mantener la lista en un solo lugar. En teoría, incluso podría pegar la lista en un archivo y poner cat nvpairfilename entre "in" y "do".

Esto es más o menos cómo se solía hacer la configuración del terminal de login shell: un script generaría estados que se ejecutarían en el shell de login. Un alias generalmente se usaría para hacer la invocación simple, como en "tset vt100". Como se mencionó en otra respuesta, también hay una funcionalidad similar en el servidor de noticias INN UseNet.

 52
Author: Thomas Kammeyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-20 14:51:54

En mi .bash_profile tengo :

# 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
}

Así que cuando quiero desactivar el proxy, las funciones se ejecutan en el shell de inicio de sesión y establece las variables como esperaba y quería.

 44
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-17 16:22:33

Es "algo" posible usando gdb y setenv(3), aunque me cuesta recomendarlo. (Además, es decir, el ubuntu más reciente realmente no te permitirá hacer esto sin decirle al núcleo que sea más permisivo con ptrace, y lo mismo puede pasar con otras distribuciones también).

$ 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
Author: Kjetil Joergensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-08 22:03:34

Esto funciona - no es lo que usaría, pero 'funciona'. Vamos a crear un script teredo para establecer la variable de entorno TEREDO_WORMS:

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

Será interpretado por el intérprete de órdenes Korn, exportará la variable de entorno y luego se reemplazará por un nuevo intérprete de órdenes interactivo.

Antes de ejecutar este script, tenemos SHELL establecido en el entorno a la shell C, y la variable de entorno TEREDO_WORMS no está establecida:

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

Cuando se ejecuta el script, estás en un nuevo shell, otro shell C interactivo, pero la variable de entorno está establecida:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

Cuando sales de este shell, el shell original se hace cargo:

% exit
% env | grep TEREDO
%

La variable de entorno no se establece en el entorno del shell original. Si usa exec teredo para ejecutar el comando, entonces el shell interactivo original es reemplazado por el shell Korn que establece el entorno, y luego ese a su vez es reemplazado por un nuevo shell interactivo C:

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

Si escribe exit (o Control-D), entonces tu shell sale, probablemente registrándote fuera de esa ventana, o llevándote de vuelta al nivel anterior de shell desde donde comenzaron los experimentos.

El mismo mecanismo funciona para Bash o Korn shell. Usted puede encontrar que el indicador después de los comandos de salida aparece en lugares divertidos.


Nótese la discusión en los comentarios. Esta no es una solución que recomendaría, pero logra el propósito declarado de un solo script para establecer el entorno que funciona con todos los shells (que aceptan la opción -i para hacer un shell interactivo). También puede agregar "$@" después de la opción de retransmitir cualquier otro argumento, lo que podría hacer que el shell se pueda usar como una herramienta general de 'establecer entorno y ejecutar comando'. Es posible que desee omitir el -i si hay otros argumentos, que conducen a:

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

El bit "${@-'-i'}" significa ' si la lista de argumentos contiene al menos un argumento, use la lista de argumentos original; de lo contrario, sustituya -i por el no existente argumento'.

 12
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-07 16:18:34

Debe usar módulos, consulte http://modules.sourceforge.net /

EDITAR: El paquete de módulos no se ha actualizado desde 2012, pero todavía funciona bien para los conceptos básicos. Todas las nuevas características, campanas y silbatos suceden en lmod este día (que me gusta más): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

 11
Author: Davide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-06 14:29:48

Otra solución que no veo mencionada es escribir el valor de la variable en un archivo.

Me encontré con un problema muy similar en el que quería ser capaz de ejecutar la última prueba de conjunto (en lugar de todas mis pruebas). Mi primer plan era escribir un comando para establecer la variable env TESTCASE, y luego tener otro comando que usaría esto para ejecutar la prueba. No hace falta decir que tuve exactamente el mismo problema que usted.

Pero entonces se me ocurrió este simple truco:

Primero mando ( testset ):

#!/bin/bash

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

Segundo comando (testrun ):

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE
 8
Author: dkinzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-26 19:21:34

Agregue la bandera-l en la parte superior de su script bash, es decir,

#!/usr/bin/env bash -l

...

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

Los valores con NAME1 y NAME2 ahora se han exportado a su entorno actual, sin embargo estos cambios no son permanentes. Si desea que sean permanentes, debe agregarlos a su archivo .bashrc u otro archivo de inicio.

De las páginas de manual:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).
 4
Author: cristobal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-16 12:43:43

Puede indicar al proceso hijo que imprima sus variables de entorno (llamando a "env"), luego haga un bucle sobre las variables de entorno impresas en el proceso padre y llame a "export" en esas variables.

El siguiente código se basa en Capturando la salida de find . - print0 en una matriz bash

Si el shell padre es el bash, puede usar

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

Si el shell padre es el guión, entonces read no proporciona la bandera-d y el código obtiene más complicado

TMPDIR=$(mktemp -d)
mkfifo $TMPDIR/fifo
(bash -s << "EOF"
    export VARNAME=something
    while IFS= read -r -d $'\0' 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
Author: klaus se,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:53

Puede invocar otro Bash con el diferente bash_profile. Además, puede crear un bash_profile especial para usarlo en un entorno multi-bashprofile.

Recuerde que puede usar funciones dentro de bashprofile, y que las funciones estarán disponibles globalmente. por ejemplo, "function user { export USER_NAME} 1}" puede establecer variables en tiempo de ejecución, por ejemplo: user olegchir && env | grep olegchir

 2
Author: Oleg Chirukhin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-29 06:15:02

Técnicamente, eso es correcto only solo 'eval' no bifurca otro shell. Sin embargo, desde el punto de vista de la aplicación que está tratando de ejecutar en el entorno modificado, la diferencia es nil: el hijo hereda el entorno de su padre, por lo que el entorno (modificado) se transmite a todos los procesos descendentes.

Ipso facto, la variable de entorno cambiada 'sticks' as siempre y cuando se esté ejecutando bajo el programa padre/shell.

Si es absolutamente necesario para la variable de entorno para permanecer después de que el padre (Perl o shell) haya salido, es necesario que el padre shell haga el trabajo pesado. Un método que he visto en la documentación es que el script actual genere un archivo ejecutable con el lenguaje 'export' necesario, y luego engañe al shell padre para que lo ejecute being siendo siempre consciente del hecho de que necesita prefacio al comando con 'source' si está tratando de dejar una versión no volátil del entorno modificado tras. Un Kluge como mucho.

El segundo método es modificar el script que inicia el entorno de shell (.bashrc o lo que sea) para contener el parámetro modificado. Esto puede ser peligroso if si manosea el script de inicialización puede hacer que su shell no esté disponible la próxima vez que intente iniciar. Hay un montón de herramientas para modificar el shell actual; mediante la fijación de los ajustes necesarios para el 'lanzador' efectivamente empujar esos cambios hacia adelante también. Generalmente no es una buena idea; si solo necesita los cambios de entorno para una suite de aplicaciones en particular, tendrá que regresar y devolver el script de inicio de shell a su estado prístino (usando vi o lo que sea) después.

En resumen, no hay métodos buenos (y fáciles). Presumiblemente esto se hizo difícil para garantizar la seguridad del sistema no se vio irrevocablemente comprometida.

 1
Author: David Lovering,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 13:23:39

La respuesta corta es no, no puede alterar el entorno del proceso padre, pero parece que lo que desea es un entorno con variables de entorno personalizadas y el shell que el usuario ha elegido.

Entonces, ¿por qué no simplemente algo como

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

Luego, cuando haya terminado con el entorno, simplemente exit.

 1
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-28 06:41:39

Siempre puedes usar alias

alias your_env='source ~/scripts/your_env.sh'
 1
Author: user1667208,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-10 23:39:37

Otra opción es usar "Módulos de entorno" ( http://modules.sourceforge.net / ). Desafortunadamente, esto introduce un tercer idioma en la mezcla. Se define el entorno con el lenguaje de Tcl, pero hay algunos comandos útiles para las modificaciones típicas (anteponer vs.anexar vs set). También necesitará tener módulos de entorno instalados. A continuación, puede usar module load *XXX* para nombrar el entorno que desee. El comando module es básicamente un alias elegante para el mecanismo eval descrito arriba por Thomas Kammeyer. La principal ventaja aquí es que puede mantener el entorno en un solo idioma y confiar en "Módulos de entorno" para traducirlo a sh, ksh, bash, csh, tcsh, zsh, python (?!?!!), sucesivamente.

 1
Author: Howard Hobbes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-23 20:02:45

Hice esto hace muchos años. Si recuerdo correctamente, incluí un alias en cada uno de ellos .bashrc y .cshrc, con parámetros, aliasing las formas respectivas de establecer el entorno a una forma común.

Entonces el script que obtendrá en cualquiera de los dos shells tiene un comando con esa última forma, que es adecuado con alias en cada shell.

Si encuentro los alias concretos, los publicaré.

 1
Author: sancho.s,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 15:14:45

He creado una solución usando pipes, eval y 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

Podría funcionar con cualquier comando.

 1
Author: Luiz Angelo Daros de Luca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-21 21:43:53

En OS X bash puede hacer lo siguiente:
Crear el archivo de script bash para desajustar la variable

#!/bin/bash
unset http_proxy

Hacer el archivo ejecutable

sudo chmod 744 unsetvar

Crear alias

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

Debería estar listo para usar siempre que tenga la carpeta que contiene su archivo de script anexado a la ruta.

 0
Author: Marton Tatai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 09:26:51

No veo ninguna respuesta que documente cómo solucionar este problema con los procesos de cooperación. Un patrón común con cosas como ssh-agent es hacer que el proceso hijo imprima una expresión que el padre puede eval.

bash$ eval $(shh-agent)

Por ejemplo, ssh-agent tiene opciones para seleccionar la sintaxis de salida compatible con Csh o 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;

(Esto hace que el agente comience a ejecutarse, pero no le permite usarlo realmente, a menos que ahora copie y pegue esta salida en su línea de comandos de shell.) Comparar:

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

(Como puedes ver, csh y tcsh usa setenv para establecer variables.)

Su propio programa también puede hacer esto.

bash$ foo=$(makefoo)

Su script makefoo simplemente calcularía e imprimiría el valor, y permitiría al llamador hacer lo que quiera con él assigning asignarlo a una variable es un caso de uso común, pero probablemente no es algo que desee codificar en la herramienta que produce el valor.

 0
Author: tripleee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-23 09:25:36

No es lo que yo llamaría sobresaliente, pero esto también funciona si necesita llamar al script desde el shell de todos modos. No es una buena solución, pero para una sola variable de entorno estática, funciona lo suficientemente bien.

1.) Crear un script con una condición que sale 0 (Exitoso) o 1 (No exitoso)

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

2.) Cree un alias que dependa del código de salida.

alias='myscript.sh && export MyVariable'

Se llama al alias, que llama al script, que evalúa la condición, que es se requiere salir de cero a través del '& & ' para establecer la variable de entorno en el shell padre.

Esto es restos flotantes, pero puede ser útil en un apuro.

 0
Author: user1802263,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-31 15:50:33

Aparte de los condicionales de writings dependiendo del valor de SHELL SHELL/TERM TERM, no. ¿Qué hay de malo en usar Perl? Es bastante omnipresente (no puedo pensar en una sola variante de UNIX que no lo tenga), y te ahorrará el problema.

 -9
Author: phresus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-30 19:08:12