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)
)echo $(x=2; echo $x)
, el fragmento$(x=2; echo $x)
necesita ser expandido. Esto requiere evaluar el comandox=2; echo $x
. La expansión de$x
ocurre durante esta evaluación, después de evaluar la partex=2
.x
unset)echo $(echo foo >somefile)${x-$(cat somefile)}
oecho $(echo $x),${x=1}
../file
no se ejecuta en una subshell. Consulte también unix.stackexchange.com/q/261638 y unix.stackexchange.com/a/157962Obviamente, sí, como dice toda la documentación, un comando entre paréntesis se ejecuta en una subshell.
La subshell hereda una copia de todas las variables del padre. La diferencia es que cualquier cambio que realice en la subshell tampoco se realiza en el padre.
La página de manual de ksh lo hace un poco más claro que el bash:
man ksh
:man bash
:fuente
When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following.
, que contiene el elemento:· shell variables and functions marked for export, along with variables exported for the command, passed in the environment
(de la mismaman bash
sección) que explica por qué unecho $x
script no imprime nada six
no se exporta.