¿Por qué es una variable visible en una subshell?

18

Learning Bash Book menciona que una subshell heredará solo variables de entorno y descriptores de archivos, etc., y que no heredará variables que no se exportan:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Como sé, el shell creará dos subshell para ()y para ./file, pero ¿por qué en el ()caso el subshell identifica la varvariable aunque no se exporta y en el ./filecaso no la identifica?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

Traté de usar stracepara descubrir cómo sucede esto y, sorprendentemente, descubrí que bash usará los mismos argumentos para la llamada al sistema de clonación, por lo que esto significa que tanto el proceso bifurcado ()como ./fileel mismo deben tener el mismo espacio de dirección del proceso del padre, entonces ¿por qué? en el ()caso, ¿la variable es visible para la subshell y no ocurre lo mismo para el ./filecaso, aunque los mismos argumentos se basan en la llamada al sistema de clonación?

usuario3718463
fuente
Vinc17 decir verdad, incluso si obtienes pstree cuando tienes subshell, crees que este asunto.
PersianGulf

Respuestas:

15

El libro Learning Bash está mal. Las subcapas heredan todas las variables. Incluso $$(el PID del shell original) se mantiene. La razón es que para un subshell, el shell simplemente se bifurca y no ejecuta un nuevo shell (por el contrario, cuando escribe ./file, se ejecuta un nuevo comando, por ejemplo, un nuevo shell; en la salida de strace, mire execvey similar) . Entonces, básicamente, es solo una copia (con algunas diferencias documentadas).

Nota: esto no es específico de bash; Esto es cierto para cualquier shell.

vinc17
fuente
Oky, pero traté de lanzar una capa en el shell e intenté ejecutar ./file pero no puedo encontrar ninguna llamada para exec, por lo que el espacio de direcciones debería ser el mismo para ambos procesos, ¿cómo se puede explicar esto?
user3718463
@ user3718463 ¿Usó la -fopción de stracerastrear niños también? Eso es necesario para encontrar los ejecutivos.
vinc17
sí, lo descubro, muchas gracias, me faltaba la opción -f, por lo que no puedo encontrar la llamada del sistema ejecutivo
user3718463
16

Tanto usted como el libro están confundiendo un subshell con un subproceso que es un shell.

Algunas construcciones de shell dan como resultado que el shell bifurque un proceso hijo. En Linux, forkes un caso especial de la clonellamada al sistema más general , que observó en el straceregistro. El niño ejecuta una parte del script de shell. El proceso hijo se llama subshell . La construcción más directa es command1 &: se command1ejecuta en un subshell y los comandos posteriores se ejecutan en el shell principal. Otras construcciones que crean un subshell incluyen la sustitución de comandos $(command2)y tuberías command3 | command4(se command3ejecuta en un subshell, se command4ejecuta en un subshell en la mayoría de los shells pero no en ksh o zsh).

Una subshell es una copia del proceso padre, por lo que no solo tiene las mismas variables de entorno, sino también las mismas definiciones internas: variables (incluido $$el ID de proceso del proceso de shell original), funciones, alias, opciones, etc. Antes de ejecutar el código en el subshell, bash establece la variable BASHPIDen el ID del proceso secundario.

Cuando ejecuta ./file, esto ejecuta un comando externo. Primero, el shell bifurca un proceso hijo; entonces este proceso hijo ejecuta (con la execvellamada al sistema) el archivo ejecutable ./file. Un proceso hijo hereda los atributos de proceso de sus padres: entorno, directorio actual, etc. Los aspectos internos de la aplicación se pierden en la execvellamada: las variables, funciones, etc. no exportadas son nociones bash que el núcleo no conoce, y se pierden cuando bash ejecuta otro programa. Incluso si ese otro programa resulta ser un script bash, es ejecutado por una nueva instancia de bash que no sabe o no le importa que su proceso padre también sea una instancia de bash. Por lo tanto, una variable de shell (variable no exportada) no sobrevive execve.

Gilles 'SO- deja de ser malvado'
fuente
Esta respuesta me aclaró algunas cosas. Lo único que no entiendo es esta oración en el segundo párrafo: "El niño ejecuta una parte del script de shell". ¿A qué script de shell se hace referencia?
flow2k
@ flow2k El script (es decir, el programa) que el shell está interpretando.
Gilles 'SO- deja de ser malvado'