¿Puede un script de shell establecer variables de entorno del shell que realiza la llamada?

Intento 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 lo configura durante la ejecución del script.

eso ya lo se

 source myscript 

ejecutará los comandos del script en lugar de ejecutar un nuevo shell, y eso puede dar como resultado la configuración del entorno del “llamante”.

Pero aquí está el problema:

Quiero que este script sea invocable desde bash o csh. En otras palabras, quiero que los usuarios de cualquiera de los shell puedan ejecutar mi script y cambien el entorno de su shell. Así que ‘fuente’ no funcionará para mí, ya que un usuario que ejecuta csh no puede generar un script bash, y un usuario que ejecuta bash no puede generar un script csh.

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

Su proceso de shell tiene una copia del entorno de los padres y no tiene acceso al entorno del proceso principal en absoluto. Cuando el proceso de shell finaliza, se pierden los cambios que ha realizado en su entorno. Obtener un archivo de script es el método más comúnmente utilizado para configurar un entorno de shell; es posible que solo quiera morder la bala y mantener una para cada uno de los dos tipos de shell.

Utilice la syntax 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 he aquí cómo hacerlo si está 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 sucedería si lo hiciera ./set_env_vars.sh ). Debido a que se ejecuta en el mismo shell, las variables ambientales que establezca estarán disponibles cuando se cierre.

Esto es lo mismo que llamar al source set_env_vars.sh , pero es más corto de escribir y podría funcionar en algunos lugares donde el source no.

No podrá modificar el shell de la persona que llama porque está en un contexto de proceso diferente. Cuando los procesos secundarios heredan las variables de su caparazón, heredan copias ellos mismos.

Una cosa que puede hacer es escribir un script que emita los comandos correctos para tcsh o sh en función de cómo se invoque. Si su script está “configurado”, haga lo siguiente:

 ln -s setit setit-sh 

y

 ln -s setit setit-csh 

Ahora bien directamente o en un alias, haz esto desde sh

 eval `setit-sh` 

o esto de csh

 eval `setit-csh` 

setit usa $ 0 para determinar su estilo de salida.

Esto es una reminiscencia de cómo la gente usa para obtener la variable de entorno TERM establecida.

La ventaja aquí es que Setit se acaba de escribir en el 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 anteriormente, y la evaluación de la expresión backquoted, esto tiene el resultado deseado.

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

 alias dosetit 'eval `setit-csh`' 

o para sh, bash, y similares:

 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 solía hacerse la configuración del terminal de inicio de sesión: una secuencia de comandos generaría sentencias que se ejecutarían en el shell de inicio de sesión. Un alias generalmente se usaría para hacer la invocación simple, como en “tset vt100”. Como se menciona en otra respuesta, también hay una funcionalidad similar en el servidor de noticias de INN UseNet.

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 } 

Entonces, cuando quiero deshabilitar el proxy, la (s) función (es) se ejecutan en el shell de inicio de sesión y establece las variables como se espera y se desea.

Es “posible” mediante el uso de gdb y setenv (3) , aunque me cuesta recomendar que realmente lo haga. (Además, es decir, el ubuntu más reciente no te permitirá hacer esto sin decirle al kernel que sea más permisivo con ptrace, y lo mismo puede ocurrir con otras distribuciones).

 $ cat setfoo #! /bin/bash gdb /proc/${PPID}/exe ${PPID} </dev/null call setenv("foo", "bar", 0) END $ echo $foo $ ./setfoo $ echo $foo bar 

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 shell Korn, exportará la variable de entorno y luego se reemplazará por un nuevo shell interactivo.

Antes de ejecutar este script, tenemos SHELL configurado en el entorno para el shell C, y la variable de entorno TEREDO_WORMS no está configurada:

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

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

 % teredo % env | grep TEREDO TEREDO_WORMS=ukelele % 

Cuando sale de este caparazón, el caparazón 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 se reemplaza por el shell Korn que establece el entorno, y luego, a su vez, se reemplaza por un nuevo shell C interactivo:

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

Si escribe exit (o Control-D ), su shell se cierra, lo que probablemente le desconecta de esa ventana, o lo lleva de vuelta al nivel previo de shell desde donde comenzaron los experimentos.

El mismo mecanismo funciona para Bash o Korn shell. Puede encontrar que la solicitud después de los comandos de salida aparece en lugares divertidos.


Tenga en cuenta la discusión en los comentarios. Esta no es una solución que recomendaría, pero logra el propósito declarado de un único script para configurar el entorno que funciona con todos los shells (que aceptan la opción -i para crear un shell interactivo). También podría agregar "$@" después de la opción de retransmitir cualquier otro argumento, lo que luego podría hacer que el shell se pueda utilizar como una herramienta general de “configurar el entorno y ejecutar comandos”. 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 que si la lista de argumentos contiene al menos un argumento, use la lista de argumentos original; de lo contrario, sustituya -i por los argumentos inexistentes ‘.

Deberías usar módulos, ver http://modules.sourceforge.net/

EDITAR: El paquete de módulos no se ha actualizado desde 2012, pero sigue funcionando 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

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 poder ejecutar la última prueba establecida (en lugar de todas mis pruebas). Mi primer plan fue escribir un comando para configurar la variable env TESTCASE, y luego tener otro comando que usaría esto para ejecutar la prueba. No hace falta decir que tuve el mismo problema exacto que tú.

Pero luego se me ocurrió este truco simple:

Primer comando (conjunto de 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 

Agregue el indicador -l en la parte superior de su secuencia de comandos bash, es decir,

 #!/usr/bin/env bash -l ... export NAME1="VALUE1" export NAME2="VALUE2" 

Los valores con NAME1 y NAME2 ahora se habrán 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 init.

De las páginas man:

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

Puede indicar al proceso hijo que imprima sus variables de entorno (llamando a “env”), luego recorrer las variables de entorno impresas en el proceso principal y llamar a “exportar” esas variables.

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

Si el shell principal es el bash, puedes usar

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

Si el shell principal es el guión, entonces read no proporciona el distintivo -d y el código se vuelve 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 

Puedes invocar a otro Bash con los diferentes bash_profile. Además, puede crear bash_profile especial para usar en el entorno multi-bashprofile.

Recuerde que puede usar funciones dentro de bashprofile, y que las funciones se podrán consultar globalmente. por ejemplo, “usuario de función {export USER_NAME $ 1}” puede establecer la variable en tiempo de ejecución, por ejemplo: usuario olegchir && env | grep olegchir

Técnicamente, eso es correcto, solo ‘eval’ no bifurca otro caparazón. Sin embargo, desde el punto de vista de la aplicación que intenta ejecutar en el entorno modificado, la diferencia es nula: el elemento secundario hereda el entorno de su elemento principal, por lo que el entorno (modificado) se transmite a todos los procesos descendentes.

Ipso facto, la variable de entorno modificada ‘se pega’, siempre y cuando se ejecute bajo el progtwig / shell principal.

Si es absolutamente necesario que la variable de entorno permanezca después de que el elemento primario (Perl o shell) haya salido, es necesario que el shell primario haga el trabajo pesado. Un método que he visto en la documentación es que la secuencia de comandos actual genere un archivo ejecutable con el idioma de ‘exportación’ necesario, y luego engaña al caparazón padre para que lo ejecute; siempre consciente del hecho de que debe preceder al ordene con ‘fuente’ si está tratando de dejar atrás una versión no volátil del entorno modificado. Un Kluge en el mejor de los casos.

El segundo método es modificar el script que inicia el entorno del shell (.bashrc o lo que sea) para contener el parámetro modificado. Esto puede ser peligroso: si manipula la secuencia de comandos de inicialización puede hacer que su caparazón no esté disponible la próxima vez que intente iniciarse. Hay muchas herramientas para modificar el shell actual; al colocar los ajustes necesarios en el “iniciador”, también empuja esos cambios hacia adelante. Generalmente no es una buena idea; Si solo necesita los cambios de entorno para un conjunto de aplicaciones particular, tendrá que regresar y devolver el script de inicio del 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 que la seguridad del sistema no se viera comprometida irrevocablemente.

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

Entonces, ¿por qué no simplemente algo así como

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

Luego, cuando haya terminado con el medio ambiente, simplemente exit .

Siempre puedes usar alias

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

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

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

A continuación, el script que obtendrá en cualquiera de las dos shells tendrá un comando con esa última forma, que es adecuado con alias en cada shell.

Si encuentro los alias concretos, los publicaré.

Creé 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.

En OS X bash, puede hacer lo siguiente:
Crea el archivo de script bash para desarmar 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.

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 syntax 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 el indicador de comandos de su shell). Compare:

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

(Como puede ver, csh y tcsh usan setenv para establecer varibles).

Tu propio progtwig puede hacer esto también.

 bash$ foo=$(makefoo) 

Su script makefoo simplemente calcularía e imprimiría el valor, y dejaría que la persona que llama hiciera lo que quisieran con él. Asignarlo a una variable es un caso de uso común, pero probablemente no sea algo que quiera codificar en la herramienta que produce el valor.

No es lo que llamaría excepcional, 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ático, funciona bastante bien.

1.) Cree un script con una condición que salga 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' 

Llama al alias, que llama al script, que evalúa la condición, que es necesario para salir de cero a través de ‘&&’ para establecer la variable de entorno en el shell primario.

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

Aparte de las escrituras condicionales según lo que $ SHELL / $ TERM esté configurado, no. ¿Qué pasa con el uso de Perl? Es bastante omnipresente (no puedo pensar en una sola variante de UNIX que no lo tenga), y te ahorrará el problema.