scripts script resueltos español ejercicios ejemplos ejecutar comandos comando basicos linux bash exec flow

español - scripts linux ejercicios resueltos



Se necesitan explicaciones para el comportamiento del comando de Linux bash builtin exec (1)

Desde el Manual de referencia de Bash , recibo lo siguiente sobre el comando incorporado de basc bash:

Si se proporciona el comando, reemplaza el shell sin crear un nuevo proceso.

Ahora tengo el siguiente script bash :

#!/bin/bash exec ls; echo 123; exit 0

Esto ejecutado, obtuve esto:

cleanup.sh ex1.bash file.bash file.bash~ output.log (files from the current directory)

Ahora, si tengo este script:

#!/bin/bash exec ls | cat echo 123 exit 0

Obtengo el siguiente resultado:

cleanup.sh ex1.bash file.bash file.bash~ output.log 123

Mi pregunta es:

Si cuando se invoca exec , reemplaza el shell sin crear un nuevo proceso , ¿por qué cuando se pone | cat | cat , el echo 123 está impreso, pero sin él, no lo está. Entonces, me gustaría que alguien me explique cuál es la lógica de este comportamiento.

Gracias.

EDITAR: Después de la respuesta de @torek, tengo un comportamiento aún más difícil de explicar:

1. el exec ls>out crea el archivo de out y coloca el resultado del comando ls ;

2. exec ls>out1 ls>out2 crea solo los archivos, pero no coloca ningún resultado. Si el comando funciona como se sugiere, creo que el comando número 2 debería tener el mismo resultado que el comando número 1 (aún más, creo que no debería haber creado el archivo out2 ).


En este caso particular, tiene el exec en una tubería. Para ejecutar una serie de comandos de canalización, el shell inicialmente debe bifurcarse, formando un subconjunto. (Específicamente tiene que crear la tubería, luego la horquilla, para que todo lo que se ejecute "a la izquierda" de la tubería pueda enviar su salida a lo que esté "a la derecha" de la tubería).

Para ver que esto es, de hecho, lo que está sucediendo, compare:

{ ls; echo this too; } | cat

con:

{ exec ls; echo this too; } | cat

El primero ejecuta ls sin salir del subconjunto, por lo que este subconjunto todavía está alrededor para ejecutar el echo . Este último ejecuta ls dejando el subconjunto, que por lo tanto ya no está allí para hacer el echo , y this too se imprime.

(El uso de { cmd1; cmd2; } normalmente suprime la acción de la horquilla del subconjunto que se obtiene con paréntesis (cmd1; cmd2) , pero en el caso de una tubería, la horquilla es "forzada", por así (cmd1; cmd2) .)

La redirección del shell actual ocurre solo si no hay "nada para ejecutar", por así decirlo, después de la palabra " exec . Por lo tanto, por ejemplo, exec >stdout 4<input 5>>append modifica el shell actual, pero exec foo >stdout 4<input 5>>append intenta ejecutar el comando foo . [Nota: esto no es estrictamente exacto; ver apéndice.]

Curiosamente, en un shell interactivo, después de que la exec foo >output falle porque no hay ningún comando foo , el shell se mantiene, pero stdout permanece redirigido a la output archivo. (Puede recuperar con exec >/dev/tty . En un script, el error al exec foo termina el script.)

Con una punta de la gorra para @Pumbaa80, aquí hay algo aún más ilustrativo:

#! /bin/bash shopt -s execfail exec ls | cat -E echo this goes to stdout echo this goes to stderr 1>&2

(nota: cat -E se simplifica desde mi habitual cat -vET , que es mi práctico cat -vET para "déjame ver caracteres no impresos de una forma reconocible"). Cuando se ejecuta este script, el resultado de ls tiene cat -E aplicado (en Linux esto hace que el final de línea sea visible como un signo $), pero el resultado enviado a stdout y stderr (en las dos líneas restantes) no se redirige . Cambiar el | cat -E | cat -E to > out y, después de ejecutar el script, observe los contenidos del archivo out : los dos echo finales no están ahí.

Ahora cambie el ls a foo (o algún otro comando que no se encuentre) y ejecute el script nuevamente. Esta vez la salida es:

$ ./demo.sh ./demo.sh: line 3: exec: foo: not found this goes to stderr

y el archivo ahora tiene los contenidos producidos por la primera línea de echo .

Esto hace que lo que el exec "realmente hace" sea lo más obvio posible (pero no más obvio, como Albert Einstein no lo expresó :-)).

Normalmente, cuando el shell va a ejecutar un "comando simple" (consulte la página del manual para la definición precisa, pero esto excluye específicamente los comandos en una "interconexión"), prepara cualquier operación de redirección de E / S especificada con < , > , y así sucesivamente abriendo los archivos necesarios. Luego, el intérprete de comandos invoca fork (o una variante equivalente pero más eficiente como vfork o clone según el sistema operativo subyacente, la configuración, etc.) y, en el proceso secundario, reorganiza los descriptores de archivos abiertos (utilizando llamadas dup2 o equivalentes) para lograr el arreglos finales deseados: > out mueve el descriptor abierto a fd 1-stdout-while 6> out mueve el descriptor abierto a fd 6.

Sin embargo, si especifica la palabra clave exec , el shell suprime el paso de la fork . Realiza la apertura de todos los archivos y la reorganización del descriptor de archivo como de costumbre, pero esta vez afecta a todos los comandos posteriores . Finalmente, después de haber hecho todas las redirecciones, el shell intenta execve() (en el sentido de llamada al sistema) el comando, si hay uno. Si no hay ningún comando, o si la llamada a execve() falla y se supone que el intérprete continuará ejecutándose (es interactivo o configuró execfail ), el intérprete se execfail . Si el execve() tiene éxito, el shell ya no existe, habiendo sido reemplazado por el nuevo comando. Si execfail está configurado y el shell no es interactivo, el shell se cierra.

(También existe la complicación adicional de la función de shell command_not_found_handle : el ejecutor de bash parece suprimir su ejecución, en función de los resultados de la prueba. La palabra clave exec en general hace que el shell no vea sus propias funciones, es decir, si tiene una función de shell f, ejecutar f como un comando simple ejecuta la función de shell, al igual que (f) que lo ejecuta en un subconjunto, pero al ejecutar (exec f) salta sobre él.)

En cuanto a por qué ls>out1 ls>out2 crea dos archivos (con o sin un exec ), esto es bastante simple: el shell abre cada redirección, y luego utiliza dup2 para mover los descriptores de archivos. Si tiene dos > redireccionamientos ordinarios, el shell abre ambos, mueve el primero a fd 1 (stdout), luego mueve el segundo a fd 1 (stdout nuevamente), cerrando el primero en el proceso. Finalmente, ejecuta ls ls , porque eso es lo que queda después de eliminar el >out1 >out2 . Mientras no haya un archivo llamado ls , el comando ls queja a stderr y no escribe nada en stdout.