¿Es $ () una subshell?

Respuestas:

75

$(…)es un subshell por definición: es una copia del estado de ejecución del shell¹, y los cambios en el estado realizado en el subshell no tienen impacto en el padre. Una subshell generalmente se implementa bifurcando un nuevo proceso (pero algunas shells pueden optimizar esto en algunos casos).

No es un subshell del que puede recuperar valores variables. Si los cambios en las variables tuvieran un impacto en el padre, no sería una subshell. Es un subshell cuya salida puede recuperar el padre. El subshell creado por $(…)tiene su salida estándar establecida en una tubería, y el padre lee de esa tubería y recoge la salida.

Hay varias otras construcciones que crean una subshell. Creo que esta es la lista completa de bash:

  • Subshell para agrupar : ( … )no hace más que crear un subshell y esperar a que termine). Contraste con { … }qué grupos ordena únicamente para fines sintácticos y no crea una subshell.
  • Antecedentes : … &crea una subshell y no espera a que termine.
  • Canalización : … | …crea dos subcapas, una para el lado izquierdo y otra para el lado derecho, y espera a que ambas terminen. El shell crea una tubería y conecta la salida estándar del lado izquierdo al extremo de escritura de la tubería y la entrada estándar del lado derecho al extremo de lectura. En algunos shells (ksh88, ksh93, zsh, bash con la lastpipeopción establecida y efectiva), el lado derecho se ejecuta en el shell original, por lo que la construcción de la tubería solo crea un subshell.
  • Sustitución de comandos : $(…)(también deletreado `…`) crea una subshell con su salida estándar establecida en una tubería, recopila la salida en el padre y se expande a esa salida, menos sus nuevas líneas finales. (Y el resultado puede estar más sujeto a la división y el bloqueo, pero esa es otra historia).
  • Sustitución de proceso : <(…)crea una subshell con su salida estándar establecida en una tubería y se expande al nombre de la tubería. El padre (o algún otro proceso) puede abrir la tubería para comunicarse con la subshell. >(…)hace lo mismo pero con la tubería en la entrada estándar.
  • Coproceso : coproc …crea una subshell y no espera a que termine. La entrada y salida estándar del subshell se configuran en una tubería con el padre conectado al otro extremo de cada tubería.

¹ En lugar de ejecutar un shell separado .

Gilles 'SO- deja de ser malvado'
fuente
¿Podría incluir también ${...}en la respuesta?
user1717828
3
@ user1717828 ¿Qué? ¿Por qué? ¿Qué tiene que ver remotamente la expansión variable con esta pregunta? No voy a incluir todo el manual de shell en mi respuesta.
Gilles 'SO- deja de ser malvado'
1
¿Qué tiene que ver remotamente la expansión variable con esta pregunta? No sé, por eso pregunté :-) Así que supongo que la sustitución de llaves no se parece en nada a la sustitución de llaves.
user1717828
@ user1717828: la expansión variable no está relacionada con las subcapas; ¡es un mecanismo completamente diferente, y definitivamente vale la pena leerlo si recién estás comenzando!
0xdd
1
@EnricoMariaDeAngelis No es la única forma, pero es la más natural. Otra forma es command | { read line; … }(dependiendo del shell, linepuede o no estar disponible después de la tubería). Todas las formas implican un subshell porque el comando que produce la salida tiene que ejecutarse independientemente del shell que lee la entrada. Si el comando es puramente interno al shell (solo construcciones y construcciones de shell, no comandos externos), el shell podría no crear un subproceso, pero eso es solo una optimización, todavía crea un subshell.
Gilles 'SO- deja de ser malvado'
20

Desde la página del comando man bash (1) en bash versión 4.4, sección "EXPANSIÓN", subsección "Sustitución de comandos":

Bash realiza la expansión ejecutando commanden un entorno de subshell [...]

Ignacio Vazquez-Abrams
fuente
99
Esto también lo especifica explícitamente POSIX .
Stephen Kitt
1
Curiosamente, en CentOS 7, la página de bashmanual no menciona ninguna subshell: Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.me pregunto si fue una omisión deliberada.
dr01
66
@ dr01 Por el contrario, bash 4.4 cambió la redacción de esa oración para incluir la palabra "subshell". Fue una aclaración: el manual mencionaba explícitamente que varias otras construcciones eran subcapas, pero hasta 4.4 no estaba explícitamente establecido para la sustitución de comandos.
Gilles parada SO ser maligno '
Sí, en CentOS v7.4.1708 (bastante reciente) bash es v4.2.46.
dr01
5

Sí, ( commands... )es una bashsubshell que se ejecutará commands...en otro proceso.

La única diferencia cuando tiene $( commands... )es que esta parte del código será commands...reemplazada después de la ejecución por todo lo que commands...escribió stdout.

Iskustvo
fuente