¿Qué alcances pueden tener las variables de shell?

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

  • ¡Ah! Bien, veamos ‘ s si entiendo esto. En el shell, si digo 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 a FOO. Pero si hubiera dicho export FOO=bar, el proceso hijo (y sus descendientes) tendrían acceso a él. Uno de ellos podría, a su vez, llamar a export FOO=buzz para cambiar el valor de sus descendientes, o simplemente FOO=buzz para cambiar el valor solo para sí mismo. . ¿Eso es correcto?
  • @NathanLong Eso ‘ no es exactamente eso: en todos los shells modernos, una variable se exporta (por lo que cualquier cambio en el valor es reflejada en el entorno de los descendientes) o no exportada (lo que significa que la variable no está en el entorno). En particular, si la variable ya está en el entorno cuando se inicia el shell, se exporta.
  • Estaba un poco confundido por la oración » si un niño cambiar el valor de una variable, el valor cambiado solo es visible para ella y todos los procesos creados después de ese cambio «. Sería más correcto decir » … visible para él y todos sus procesos descendientes creados después de ese cambio » – el otro los hijos del proceso padre, incluso los que se inician después del proceso hijo, no se ven afectados.

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 a export).
  • 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 usar export 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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *