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 de hecho es lo que está sucediendo, comparar:
{ ls; echo this too; } | cat
con:
{ exec ls; echo this too; } | cat
las antiguas carreras ls
sin salir del sub-shell, por lo que este sub-shell es por lo tanto todavía en torno a 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
no se imprime.
(El uso de entre llaves { cmd1; cmd2; }
normalmente suprime la acción tenedor sub-shell que se obtiene con paréntesis (cmd1; cmd2)
, pero en el caso de un tubo, el tenedor es "forzada", por así decirlo.)
El redireccionamiento 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; véase la adición.]
Curiosamente, en un shell interactivo, después exec foo >output
falla porque no hay ningún comando foo
, los palos de concha alrededor, pero la salida estándar permanece redirigido a presentar output
. (Usted puede recuperar con exec >/dev/tty
En un script, el hecho de no exec foo
termina la secuencia de comandos..)
Con una extremidad del sombrero a @ Pumbaa80, aquí hay algo aún más ilustrativa:
#! /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áctica para "déjame ver los caracteres no impresos de una manera reconocible"). Cuando se ejecuta este script, la salida de ls
tiene aplicado cat -E
(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) es no redirigido . Cambie el | cat -E
al > out
y, después de ejecutar el script, observe el contenido del archivo out
: los dos últimos echo
s no están allí.
Ahora cambie ls
a foo
(o algún otro comando que no se encuentre) y vuelva a ejecutar el script. Esta vez, la salida es:
$ ./demo.sh
./demo.sh: line 3: exec: foo: not found
this goes to stderr
y el archivo out
ahora tiene los contenidos producidos por la primera línea de echo
.
Esto hace que lo que exec
"realmente tiene" sea lo más obvio posible (pero no es más obvio, ya que 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. A continuación, 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 (usando llamadas dup2
o equivalentes) para lograr el arreglos finales deseados: > out
mueve el descriptor abierto a fd-1-stdout, mientras 6> out
mueve el descriptor abierta a fd 6.
Si se especifica la palabra clave exec
, sin embargo, la cáscara suprime el paso fork
. Hace todo el archivo de apertura y el archivo-descriptor-reorganizar como de costumbre, pero esta vez, afecta a todos los comandos posteriores. Finalmente, habiendo 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 execve()
falla y se supone que el shell continúa ejecutándose (es interactivo o ha configurado execfail
), los soldados de shell están activados. Si el execve()
tiene éxito, el shell ya no existe, habiendo sido reemplazado por el nuevo comando. Si execfail
está desactivado y el shell no es interactivo, el shell se cierra.
(También existe la complicación añadida de la función command_not_found_handle
shell bash de exec
parece suprimir ejecutarlo, basado en los resultados de pruebas La palabra clave exec
en general hace que la cáscara no mira a sus propias funciones, es decir, si tiene una. función de línea de f, corriendo f
como un simple comando se ejecuta la función shell, al igual que (f)
que se ejecuta en un sub-shell, pero el funcionamiento (exec f)
salta sobre ella.)
cuanto a por qué
ls>out1 ls>out2
crea dos archivos (con o sin una
exec
), esto es bastante simple: el shell abre cada redirección y luego usa
dup2
para mover los descriptores de archivo. 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
. Siempre que no haya un archivo llamado
ls
, el comando
ls
se queja a stderr y no escribe nada en stdout.
¡Aprendí algo! – blueshift
Tu enlace en 'exec' es la función C' exec'. Lo que está probando es el edificio 'bash'' exec'. Ellos no son los mismos. Consulte www.gnu.org/software/bash/manual/bashref.html –
@ J-16SDiZ corregido como sugirió, pero el problema persiste – artaxerxe