Acabo de encontrarme con un problema que me muestra que «no tengo claro el alcance de las variables de shell.
Estaba intentando use bundle install
, que es un comando de Ruby que usa el valor de $GEM_HOME
para hacer su trabajo. Había configurado $GEM_HOME
, pero el comando ignoró ese valor hasta que usé export
, como en export GEM_HOME=/some/path
.
Leí que esto hace que la variable de alguna manera sea «global» (también conocida como una variable de entorno ), pero no entender lo que eso significa. Sé de los globales en programación, pero no de programas distintos.
Además, dado que mi configuración de tales variables se aplica solo a la sesión de shell actual, ¿cómo las establecería para, digamos, un proceso demonizado?
¿Qué ámbitos pueden tener las variables de shell?
Respuesta
Los procesos están organizados como un árbol: cada proceso tiene un padre único, aparte de init
que PID
es siempre 1 y no tiene padre.
La creación de un nuevo proceso generalmente pasa por un par de fork
/ execv
llamadas al sistema, donde el entorno del proceso hijo es una copia del proceso padre.
Para poner una variable en el entorno desde el shell tienes que export
esa variable, de modo que sea visible recursivamente para todos los niños. Pero tenga en cuenta que si un niño cambia el valor de una variable, el valor cambiado solo es visible para él y todos los procesos creados después de ese cambio (siendo una copia , como anteriormente dijo).
Tenga también en cuenta que un proceso hijo podría cambiar su entorno, por ejemplo, podría restablecerlo a los valores predeterminados, como probablemente se hace desde login
para ejemplo.
Comentarios
Responder
En al menos en ksh
y bash
, las variables pueden tener tres ámbitos, no dos como lo están diciendo todas las respuestas restantes.
En Además de la variable exportada (es decir, el entorno) y los ámbitos de las variables no exportadas de shell, también hay una tercera más estrecha para las variables locales de función.
Variables declaradas en funciones de shell con typeset
token solo son visibles dentro de las funciones en las que están declaradas y en (sub) funciones llamadas desde allí.
Este ksh
/ bash
código:
# Create a shell script named /tmp/show that displays the scoped variables values. echo "echo [$environment] [$shell] [$local]" > /tmp/show chmod +x /tmp/show # Function local variable declaration function f { typeset local=three echo "in function": . /tmp/show } # Global variable declaration export environment=one # Unexported (i.e. local) variable declaration shell=two # Call the function that creates a function local variable and # display all three variable values from inside the function f # Display the three values from outside the function echo "in shell": . /tmp/show # Display the same values from a subshell echo "in subshell": /tmp/show # Display the same values from a disconnected shell (simulated here by a clean environment start) echo "in other shell" env -i /tmp/show
produce esta salida:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
Como puede ver, la variable exportada se muestra desde las tres primeras ubicaciones, las variables no exportadas no se muestran fuera del shell actual y la variable local de la función no tiene valor fuera de la función en sí. La última prueba no muestra ningún valor, esto se debe a que las variables exportadas no se comparten entre los shells, es decir, solo se pueden heredar y el valor heredado no puede verse afectado posteriormente por el shell padre.
Tenga en cuenta que este último comportamiento es bastante diferente al de Windows, donde puede usar variables del sistema que son completamente globales y compartidas por todos los procesos.
Respuesta
Tienen el alcance por proceso
Los otros respondedores me ayudaron a entender que el alcance de la variable de shell se trata de procesos y sus descendientes .
Cuando escribes un comando como ls
en la línea de comandos, en realidad estás bifurcar un proceso para ejecutar el programa ls
. El nuevo proceso tiene su shell como padre.
Cualquier proceso puede tener sus propias variables «locales», que son no se pasa a los procesos secundarios. También puede establecer variables de «entorno», que son. El uso de export
crea una variable de entorno. En caso, los procesos no relacionados (pares del original) no verán la variable; solo estamos controlando qué proceso hijo
Suponga que tiene un shell bash, al que «llamaremos A. Escriba bash
, que crea un shell bash de proceso hijo, al que «llamaremos B. Todo lo que hayas llamado export
en A seguirá estando configurado en B.
Ahora, en B, dices FOO=b
. Sucederá una de dos cosas:
- Si B no recibió (de A) una variable de entorno llamada
FOO
, creará una variable local. Los hijos de B no lo obtendrán (a menos que B llame aexport
). - Si B hizo recibir (de A) una variable de entorno llamada
FOO
, la modificará por sí misma y sus hijos bifurcados posteriormente . Los hijos de B verán el valor que B asignó. Sin embargo, esto no afectará a A en absoluto.
Aquí hay una demostración rápida .
FOO=a # set "local" environment variable echo $FOO # "a" bash # forks a child process for the new shell echo $FOO # not set exit # return to original shell echo $FOO # still "a" export FOO # make FOO an environment variable bash # fork a new "child" shell echo $FOO # outputs "a" FOO=b # modifies environment (not local) variable bash # fork "grandchild" shell echo $FOO # outputs "b" exit # back to child shell exit # back to original shell echo $FOO # outputs "a"
Todo esto explica mi problema original: configuré GEM_HOME
en mi shell, pero cuando llamé bundle install
, que creó un proceso secundario. Debido a que «no usé export
, el proceso secundario no» recibió el shell «s GEM_HOME
.
Anular la exportación
Puede «anular la exportación» de una variable (evitar que se pase a los niños) utilizando export -n FOO
.
export FOO=a # Set environment variable bash # fork a shell echo $FOO # outputs "a" export -n FOO # remove environment var for children bash # fork a shell echo $FOO # Not set exit # back up a level echo $FOO # outputs "a" - still a local variable
Comentarios
- Cuando dices » lo modificará para sí mismo y sus hijos » debe aclarar que solo los hijos creados después de la modificación verá el valor modificado.
- @enzotib – buen punto. Actualizado.
Respuesta
La mejor explicación que puedo encontrar sobre la exportación es esta:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
El conjunto de variables dentro de un subshell o shell hijo solo es visible para la subcapa en la que se define. La variable exportada en realidad está hecha para ser una variable de entorno. Por lo tanto, para que quede claro, su bundle install
ejecuta su propio shell que no «ve el $GEM_HOME
a menos que sea un environment
variable también conocida como exportada.
Puede echar un vistazo a la documentación para el alcance de la variable aquí:
http://www.tldp.org/LDP/abs/html/subshells.html
Comentarios
- Ah, así que no utilicé el término » variable de entorno » para
FOO=bar
; debe usarexport
para convertirlo en uno. Pregunta corregida en consecuencia. - Eche un vistazo al enlace que agregué.
Respuesta
Existe una jerarquía de ámbitos de variables, como se esperaba.
Entorno
El ámbito más externo es el entorno. Este es el único ámbito gestionado por el sistema operativo y, por lo tanto, se garantiza que existe para cada proceso. Cuando se inicia un proceso, recibe un copia del entorno de su padre después de lo cual los dos se vuelven independientes: modificar el entorno del hijo no cambia el entorno del padre, y modificar el entorno del padre no cambia el de un hijo ya existente.
Variables de shell
Los shells tienen su propia noción de variables. Aquí es donde las cosas comienzan a volverse un poco confusas.
Cuando asigna un valor a una variable en un shell, y esa variable ya existe en el entorno, la variable de entorno recibe el nuevo valor. Sin embargo, si la variable aún no está en el entorno, se convierte en una variable de shell . Las variables de shell solo existen dentro del proceso de shell, similar a cómo las variables de Ruby solo existen dentro de un script de Ruby. Los procesos secundarios nunca los heredan.
Aquí es donde entra en juego la palabra clave export
. Copia una variable de shell en el entorno del proceso de shell, haciendo posible que los procesos secundarios hereden.
Variables locales
Las variables locales son variables de shell dentro de los bloques de código que las contienen. Declaras variables locales con la typeset
palabra clave (portátil) o local
o declare
(Bash ). Como otras variables de shell, los procesos secundarios no heredan las variables locales. Además, las variables locales no se pueden exportar.
FOO=bar
, eso establece el valor para el proceso del shell actual. Si luego ejecuto un programa como (bundle install
), eso crea un proceso secundario, que no ‘ no obtiene acceso aFOO
. Pero si hubiera dichoexport FOO=bar
, el proceso hijo (y sus descendientes) tendrían acceso a él. Uno de ellos podría, a su vez, llamar aexport FOO=buzz
para cambiar el valor de sus descendientes, o simplementeFOO=buzz
para cambiar el valor solo para sí mismo. . ¿Eso es correcto?