2012-03-29 9 views
36

Desde Bash Reference Manual me sale el siguiente golpe sobre exec orden interna:necesitan explicaciones para bash de Linux orden interna comportamiento comando exec

Si se suministra comando, se sustituye el intérprete sin crear un nuevo proceso.

Ahora tengo el siguiente bash guión:

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

Este ejecutado, tengo 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 la siguiente salida:

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

Mi pregunta es:

Si cuando se invoca execque sustituye la cáscara sin crear un nuevo proceso, por eso cuando se ponen | cat, se imprime la echo 123, pero sin ella, no lo es. Entonces, me gustaría que alguien me explique cuál es la lógica de este comportamiento.

Gracias.

EDIT: Después de la respuesta @torek, me sale un aún más difícil de explicar el comportamiento:

1. exec ls>out comando crea el archivo out y puso en ella el resultado del comando ls 's;

2. exec ls>out1 ls>out2 crea solo los archivos, pero no los inserta en 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).

+4

¡Aprendí algo! – blueshift

+1

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 –

+0

@ J-16SDiZ corregido como sugirió, pero el problema persiste – artaxerxe

Respuesta

38

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.

+1

En cuanto al shell (no) interactivo, la página de manual dice "If' command' no puede ser ejecutado por alguna razón, una shell no interactiva sale, a menos que la opción shell 'execfail' esté habilitada, en cuyo caso devuelve la falla. Un shell interactivo devuelve la falla" – user123444555621

+0

@torek mira la parte EDIT en la pregunta – artaxerxe

+0

@ Pumbaa80 : muy agradable; esto ayuda a aclarar "cómo funciona el ejecutivo". Añadiré una nota a mi respuesta. – torek

Cuestiones relacionadas