¿Los paréntesis realmente colocan el comando en una subcapa?

¿Los paréntesis realmente colocan el comando en una subcapa?

Por lo que he leído, poner un comando entre paréntesis debería ejecutarlo en un subshell, similar a ejecutar un script. Si esto es cierto, ¿cómo ve la variable x si x no se exporta?

x=1

Ejecutar (echo $x)en la línea de comando da como resultado 1

La ejecución echo $xde un script no produce nada, como se esperaba

Respuesta1

Un subshell comienza como una copia casi idéntica del proceso del shell original. Debajo del capó, el caparazón llama alforkllamada al sistema 1 , que crea un nuevo proceso cuyo código y memoria son copias 2 . Cuando se crea la subcapa, existen muy pocas diferencias entre ésta y su capa principal. En particular, tienen las mismas variables. Incluso la $$variable especial mantiene el mismo valor en los subshells: es el ID del proceso del shell original. De manera similar $PPIDes el PID del padre del shell original.

Algunos shells cambian algunas variables en el subshell. Bash ≥4.0 se establece BASHPIDen el PID del proceso de shell, que cambia en subcapas. Bash, zsh y mksh se organizan para $RANDOMproducir valores diferentes en el nivel principal y en el subnivel. Pero aparte de casos especiales integrados 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, definiciones de alias, opciones de shell y otras configuraciones también se heredan.

Un subshell creado por (…)tiene los mismos descriptores de archivo que su creador. Algunos otros medios para crear subcapas modifican algunos descriptores de archivos antes de ejecutar el código de usuario; Por ejemplo, el lado izquierdo de un tubo discurre por una subcapa 3 con salida estándar conectada al tubo. El subshell también comienza con el mismo directorio actual, la misma máscara de señal, etc. Una de las pocas excepciones es que los subshells no heredan trampas personalizadas: las señales ignoradas ( ) permanecen ignoradas en el subshell, pero otras trampas (trap '' SIGNALtrap CODESEÑAL) se restablecen a la acción predeterminada 4 .

Por tanto, una subcapa es diferente de ejecutar un script. Un script es un programa independiente. Casualmente, este programa separado podría ser también un script ejecutado 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 el intérprete del script de shell secundario esejecutado, no ve estas variables. Las variables exportadas, es decir, las variables de entorno, se transmiten a los programas ejecutados.

De este modo:

x=1
(echo $x)

se imprime 1porque el subshell es una replicación 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 xde la segunda línea no tiene más conexión con el xde la segunda línea que en

x=1
perl -le 'print $x'

o

x=1
python -c 'print x'

1 A menos que el shell optimice la bifurcación, pero la emule tanto como sea necesario para preservar el comportamiento del código que está ejecutando. Ksh93 optimiza mucho, otros shells en su mayoría no lo hacen.
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 quecosas como$(trap)Puede informar sobre las trampas del caparazón original. Tenga en cuenta también que muchos shells tienen errores en los casos de esquina que involucran trampas. Por ejemploninjaljobserva 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 ERRtrampa desde el subcapa anidado en el caso de "dos subcapas", pero no la ERRtrampa desde el subcapa intermedio; set -Ela opción debería propagar la ERRtrampa a todos los subcapas, pero el subcapa intermedio está optimizado y también lo está. No está allí para ejecutar su ERRtrampa.

Respuesta2

Obviamente, sí, como dice toda la documentación, un comando entre paréntesis se ejecuta en un subshell.

La subcapa hereda una copia de todas las variables del padre. La diferencia es que cualquier cambio que realice en el subnivel no se realiza también en el nivel principal.

La página de manual de ksh deja esto un poco más claro que la de bash:

man ksh:

Un comando entre paréntesis se ejecuta en un subshell sin eliminar las variables no exportadas.

man bash:

(lista)

list se ejecuta en un entorno de subshell (consulte ENTORNO DE EJECUCIÓN DE COMANDOS a continuación). Las asignaciones de variables y los comandos integrados que afectan el entorno del shell no permanecen vigentes una vez que se completa el comando.

ENTORNO DE EJECUCIÓN DE COMANDOS

El shell tiene un entorno de ejecución, que consta de lo siguiente: [...] parámetros del shell que se establecen mediante asignación de variables [...].
La sustitución de comandos, los comandos agrupados entre paréntesis y los comandos asíncronos se invocan en un entorno de subshell que es un duplicado del entorno de shell, [...]

información relacionada