En la terminología POSIX, un entorno de subshell está vinculado a la noción de Entorno de ejecución de Shell .
Un entorno de subshell es un entorno de ejecución de shell separado creado como un duplicado del entorno principal. Ese entorno de ejecución incluye cosas como archivos abiertos, umask, directorio de trabajo, variables de shell / funciones / alias ...
Los cambios en ese entorno de subshell no afectan al entorno principal.
Tradicionalmente en el shell Bourne o ksh88 en el que se basa la especificación POSIX, eso se hizo bifurcando un proceso hijo.
Las áreas donde POSIX requiere o permite que el comando se ejecute en un entorno de subshell son aquellas donde tradicionalmente ksh88 bifurcó un proceso de shell hijo.
Sin embargo, no obliga a las implementaciones a usar un proceso hijo para eso.
Un shell puede elegir implementar ese entorno de ejecución separado de la forma que desee.
Por ejemplo, ksh93 lo hace guardando los atributos del entorno de ejecución principal y restaurándolos al terminar el entorno de subshell en contextos donde se puede evitar la bifurcación (ya que la optimización es bastante costosa en la mayoría de los sistemas).
Por ejemplo, en:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX requiere cd /foo
que se ejecute en un entorno separado y que genere algo como:
/foo
/bar
/foo
No requiere que se ejecute en un proceso separado. Por ejemplo, si stdout se convierte en una tubería rota, la pwd
ejecución en el entorno de subshell podría muy bien enviar el SIGPIPE al único proceso de shell.
La mayoría de los shells incluidos bash
lo implementarán evaluando el código dentro (...)
de un proceso secundario (mientras que el proceso principal espera su finalización), pero ksh93 lo hará al ejecutar el código dentro (...)
, todo en el mismo proceso:
- recuerde que está en un entorno de subshell.
- luego
cd
, guarde el directorio de trabajo anterior (generalmente en un descriptor de archivo abierto con O_CLOEXEC), guarde el valor de las variables OLDPWD, PWD y cualquier cosa que cd
pueda modificar y luego realice elchdir("/bar")
- Al regresar de la subshell, el directorio de trabajo actual se restaura (con un
fchdir()
fd guardado) y todo lo demás que la subshell puede haber modificado.
Hay contextos en los que no se puede evitar un proceso hijo. ksh93 no se bifurca:
var=$(subshell)
(subshell)
Pero lo hace en
{ subshell; } &
{ subshell; } | other command
Es decir, los casos en que las cosas tienen que ejecutarse en procesos separados para que puedan ejecutarse simultáneamente.
Las optimizaciones de ksh93 van más allá de eso. Por ejemplo, mientras que en
var=$(pwd)
la mayoría de los shells bifurcan un proceso, hacen que el niño ejecute el pwd
comando con su stdout redirigido a una tubería, pwd
escribe el directorio de trabajo actual en esa tubería, y el proceso padre lee el resultado en el otro extremo de la tubería, ksh93
virtualiza todo eso de ninguna manera requiriendo la horquilla ni la tubería. Una bifurcación y una tubería solo se usarían para comandos no integrados.
Tenga en cuenta que hay otros contextos que subcapas para los cuales las bifurcaciones bifurcan un proceso secundario. Por ejemplo, para ejecutar un comando que se almacena en un ejecutable separado (y que no es un script destinado al mismo intérprete de shell), un shell tendría que bifurcar un proceso para ejecutar ese comando en él, de lo contrario no sería capaz de ejecutar más comandos después de que ese comando regrese.
En:
/bin/echo "$((n += 1))"
Eso no es un subshell, el comando se evaluará en el entorno de ejecución de shell actual, la n
variable del entorno de ejecución de shell actual se incrementará, pero el shell bifurcará un proceso secundario para ejecutar ese /bin/echo
comando en él con la expansión de $((n += 1))
como argumento .
Muchos shells implementan una optimización en el sentido de que no bifurcan un proceso secundario para ejecutar ese comando externo si es el último comando de un script o un subshell (para aquellos subshell que se implementan como procesos secundarios). ( bash
sin embargo, solo lo hace si ese comando es el único comando de la subshell).
Lo que eso significa es que, con esos shells, si el último comando en el subshell es un comando externo, el subshell no hace que se genere un proceso adicional. Si comparas:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
con
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
habrá la misma cantidad de procesos creados, solo en el segundo caso, la segunda bifurcación se realiza antes para que a=2
se ejecute en un entorno de subshell.