¿Por qué no puedo imprimir una variable que puedo ver en la salida de env?

9

Estoy interesado en establecer variables ambientales de una instancia de shell de otra. Entonces decidí investigar un poco. Después de leer una serie de preguntas sobre esto , decidí probarlo.

Engendré dos proyectiles A y B (PID 420), ambos corriendo zsh. Desde shell AI ejecutó lo siguiente.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

Desde el shell B cuando ejecuto envpuedo ver que la variable FOO está configurada con un valor de barra. Esto me hace pensar que FOO se ha inicializado con éxito en el entorno de shell B. Sin embargo, si trato de imprimir FOO obtengo una línea vacía que implica que no está configurada. Para mí, parece que hay una contradicción aquí.

Esto fue probado tanto en mi propio sistema Arch GNU / Linux como en una máquina virtual Ubuntu. También probé esto en bashdonde la variable ni siquiera apareció en env. Esto, aunque es decepcionante para mí, tiene sentido si el shell almacena en caché una copia de su entorno en el momento de la generación y solo usa eso (lo que se sugirió en una de las preguntas vinculadas). Esto todavía no responde por qué zshpuede ver la variable.

¿Por qué la salida de echo $FOOvacío?


EDITAR

Después de la entrada en los comentarios, decidí hacer un poco más de pruebas. Los resultados se pueden ver en las tablas a continuación. En la primera columna está el shell en el que FOOse inyectó la variable. La primera fila contiene el comando cuyo resultado se puede ver debajo de él. La variable FOOse inyectó usando: sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. Los comandos específicos de zsh: zsh -c '...'también se probaron con bash. Los resultados fueron idénticos, su producción se omitió por brevedad.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Lo anterior parece implicar que los resultados son independientes de la distribución. Esto no me dice mucho más zshy bashmaneja la configuración de variables de manera diferente. Además, export FOOtiene un comportamiento muy diferente en este contexto dependiendo del shell. Esperemos que estas pruebas puedan aclararle algo a alguien más.

rlf
fuente
¿Qué sucede si haces un zsh -c 'echo $FOO'(usa comillas simples) en su lugar? ¿Puedes verlo entonces?
user1934428
El valor correcto se imprime desde un nuevo sub shell (probado también para bash child). Claramente, el ambiente es persistente de alguna manera ya que el niño puede heredarlo, pero ¿por qué el padre no lo honra?
rlf
3
Es lo que pensaba. Supongo que el shell tiene en algún lugar una tabla de símbolos de variables, algunas de ellas están marcadas como "exportadas", lo que significa que al abrir un subshell, se colocan en el entorno del proceso secundario. Inicialmente (cuando se inicia el shell), las variables del entorno en ese momento se copian en la tabla de símbolos (por supuesto, también como variables "exportadas"). Cuando cambia el entorno, no se observa que el shell actualice su tabla de símbolos, pero los procesos secundarios (como env) ven el entorno modificado.
user1934428
2
Probé en Ubuntu 16.04 con zsh 5.1.1 y bash 4.3.48 (1) y parece que establecer una variable de entorno para zshen GDB no lo hace visible como una variable de shell, pero hace que se pase a procesos secundarios (como ha observado), mientras que establecer uno para bash sí lo hace visible como una variable de shell, ¡pero no hace que se pase a procesos secundarios! Parece que zsh y bash usan diferentes estrategias para administrar variables, con zsh que rastrea variables que no son de entorno y bash almacena todo en su entorno que desinfecta cuando se inicia un hijo (sin subshell).
Eliah Kagan
@EliahKagan, interesante; deberías publicar eso como respuesta. También me pregunto si se hace una diferencia si se ejecuta export FOOen bash?
Comodín

Respuestas:

2

La mayoría de los shells no usan la API getenv()/ setenv()/ putenv().

Al iniciarse, crean variables de shell para cada variable de entorno. Esos serán almacenados en estructuras internas que necesitan transportar otra información, como si la variable se exporta, solo lectura ... No pueden usar las bibliotecas environpara eso.

Del mismo modo, y por esa razón, no van a utilizar execlp(), execvp()para ejecutar comandos, pero llamar a la execve()llamada al sistema directamente, el cálculo de la envp[]matriz en base a la lista de sus variables exportadas.

Entonces, en su gdb, necesitaría agregar una entrada a esa tabla interna de variables de shells, o posiblemente llamar a la función correcta que lo haría interpretar un export VAR=valuecódigo para actualizar esa tabla por sí mismo.

En cuanto a por qué se ve una diferencia entre bashy zshcuando se llama setenv()en gdb, Sospecho que es porque usted está llamando setenv()antes de que los inicializa shell, por ejemplo al entrar main().

Notarás bash's main()is int main(int argc, char* argv[], char* envp[])(y bashasigna variables de esos entornos envp[]) mientras que zsh' s es int main(int argc, char* argv[])y zshobtiene las variables en su environlugar. setenv()modifica environpero no puede modificar envp[]en el lugar (solo lectura en varios sistemas, así como las cadenas a las que apuntan estos punteros).

En cualquier caso, después de que el shell haya leído environen el inicio, el uso setenv()sería ineficaz ya que el shell ya no usa environ(o getenv()) después.

Stéphane Chazelas
fuente