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 var
variable aunque no se exporta y en el ./file
caso 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 strace
para 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 ./file
el 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 ./file
caso, aunque los mismos argumentos se basan en la llamada al sistema de clonación?
Respuestas:
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, mireexecve
y 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.
fuente
-f
opción destrace
rastrear niños también? Eso es necesario para encontrar los ejecutivos.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,
fork
es un caso especial de laclone
llamada al sistema más general , que observó en elstrace
registro. El niño ejecuta una parte del script de shell. El proceso hijo se llama subshell . La construcción más directa escommand1 &
: secommand1
ejecuta 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íascommand3 | command4
(secommand3
ejecuta en un subshell, secommand4
ejecuta 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 variableBASHPID
en 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 laexecve
llamada 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 laexecve
llamada: 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 sobreviveexecve
.fuente