Una subshell comienza como una copia casi idéntica del proceso de shell original. Debajo del capó, el shell llama a la fork
llamada al sistema 1 , que crea un nuevo proceso cuyo código y memoria son copias 2 . Cuando se crea el subshell, hay muy pocas diferencias entre él y su padre. En particular, tienen las mismas variables. Incluso la $$
variable especial mantiene el mismo valor en subcapas: es la ID de proceso del shell original. Del mismo modo, $PPID
es el PID del padre del shell original.
Algunas shells cambian algunas variables en la subshell. Bash se establece BASHPID
en el PID del proceso de shell, que cambia en subcapas. Bash, zsh y mksh arreglan para $RANDOM
producir diferentes valores en el padre y en el subshell. Pero aparte de los casos especiales incorporados como estos, todas las variables tienen el mismo valor en el subshell que en el shell original, el mismo estado de exportación, el mismo estado de solo lectura, etc. Todas las definiciones de funciones, alias, opciones de shell y otras configuraciones también se heredan.
Una subshell creada por (…)
tiene los mismos descriptores de archivo que su creador. Algunos otros medios para crear subshells modifican algunos descriptores de archivos antes de ejecutar el código de usuario; por ejemplo, el lado izquierdo de una tubería se ejecuta en un subshell 3 con salida estándar conectada a la tubería. La subshell también comienza con el mismo directorio actual, la misma máscara de señal, etc. Una de las pocas excepciones es que las subshell no heredan trampas personalizadas: las señales ignoradas ( ) permanecen ignoradas en la subshell, pero se restablecen otras trampas ( SEÑAL ) a la acción predeterminada 4 .trap '' SIGNAL
trap CODE
Por lo tanto, una subshell es diferente de ejecutar un script. Un script es un programa separado. Este programa separado podría ser también una secuencia de comandos ejecutada por el mismo intérprete que el padre, pero esta coincidencia no le da al programa separado ninguna visibilidad especial sobre los datos internos del padre. Las variables no exportadas son datos internos, por lo que cuando se ejecuta el intérprete para el script de shell hijo , no ve estas variables. Las variables exportadas, es decir, las variables de entorno, se transmiten a los programas ejecutados.
Así:
x=1
(echo $x)
se imprime 1
porque el subshell es una réplica del shell que lo generó.
x=1
sh -c 'echo $x'
sucede que ejecuta un shell como un proceso hijo de un shell, pero el x
de la segunda línea no tiene más conexión con el x
de la segunda línea que en
x=1
perl -le 'print $x'
o
x=1
python -c 'print x'
1 Una excepción es el ksh93
shell donde se optimiza la bifurcación y se emulan la mayoría de sus efectos secundarios.
2 Semánticamente, son copias. Desde una perspectiva de implementación, se está compartiendo mucho.
3 Para el lado derecho, depende de la carcasa.
4 Si prueba esto, tenga en cuenta que cosas como$(trap)
pueden informar las trampas del shell original. Tenga en cuenta también que muchos proyectiles tienen errores en casos de esquina que involucran trampas. Por ejemplo, ninjalj señala que a partir de bash 4.3, bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'
ejecuta la ERR
trampa desde el subshell anidado en el caso de "dos subshell", pero no la ERR
trampa desde el subshell intermedio: la set -E
opción debe propagarERR
trap a todas las subcapas pero la subshell intermedia está optimizada y no está ahí para ejecutar su ERR
trampa.
x=out; (x=in; echo $x)
)