2012-03-29 15 views
36

Da Bash Reference Manual ottengo il seguente su exec bash comando incorporato:bisogno di spiegazioni per Linux bash incorporato comportamento comando exec

Se il comando viene fornito, si sostituisce la shell senza creare un nuovo processo.

Ora ho la seguente bash script:

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

Questa eseguito, ho ottenuto questo:

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

Ora, se ho questo script:

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

Ottengo il seguente risultato:

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

La mia domanda è:

Se quando exec è invocato sostituisce il guscio senza creare un nuovo processo, perché quando ha messo | cat, il echo 123 viene stampato, ma senza di esso, non lo è. Quindi, sarei felice se qualcuno potesse spiegare qual è la logica di questo comportamento.

Grazie.

EDIT: Dopo risposta @torek, ottengo un ancora più difficile da spiegare il comportamento:

1. exec ls>out comando crea il file out e mettere in esso risultato comando s' il ls;

2. exec ls>out1 ls>out2 crea solo i file, ma non inserire alcun risultato. Se il comando funziona come suggerito, penso che il comando numero 2 dovrebbe avere lo stesso risultato del comando numero 1 (ancora di più, penso che non avrebbe dovuto aver creato il file out2).

+4

Ho imparato qualcosa! – blueshift

+1

Il collegamento su 'exec' è la funzione C' exec'. Quello che stai testando è il buildin 'bash'. Exec'. Non sono la stessa cosa. Vedi www.gnu.org/software/bash/manual/bashref.html –

+0

@ J-16SDiZ corretto come suggerito, ma il problema persiste – artaxerxe

risposta

38

In questo caso particolare, si dispone del exec in una pipeline. Per eseguire una serie di comandi pipeline, la shell deve inizialmente fork, creando una sub-shell. (In particolare deve creare la pipe, quindi fork, in modo che tutto ciò che viene eseguito "sulla sinistra" della pipe possa essere inviato a qualsiasi "a destra" della pipe.)

Per vedere che questo è infatti ciò che sta accadendo, confrontare:

{ ls; echo this too; } | cat 

con:

{ exec ls; echo this too; } | cat 

Gli ex piste ls senza lasciare il sub-shell, in modo che questo sub-shell è quindi ancora in giro per eseguire il echo . Quest'ultima esegue ls lasciando la sub shell, che quindi non è più lì per fare il echo e this too non viene stampato.

(L'uso di parentesi graffe { cmd1; cmd2; } normalmente sopprime l'azione della forcella sub-shell che si ottiene con parentesi (cmd1; cmd2), ma nel caso di un tubo, la forcella è "forzato", per così dire.)

Il reindirizzamento della shell corrente avviene solo se c'è "niente da eseguire", per così dire, dopo la parola exec. Pertanto, ad esempio, exec >stdout 4<input 5>>append modifica la shell corrente, ma exec foo >stdout 4<input 5>>append tenta di eseguire il comando foo. [Nota: questo non è strettamente accurato; vedi addendum.]

È interessante notare che, in una shell interattiva, dopo exec foo >output fallisce perché non v'è alcun comando foo, i bastoni guscio attorno, ma rimane stdout reindirizzato su file output. (È possibile recuperare con exec >/dev/tty In uno script, la mancata exec foo termina lo script..)


Con una punta del cappello a @ Pumbaa80, ecco qualcosa di ancora più illustrativo:

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

(nota: cat -E è semplificato dal mio solito cat -vET, che è il mio pratico go-to per "fammi vedere i caratteri non stampabili in modo riconoscibile"). Quando viene eseguito questo script, l'output da ls è applicato a cat -E (su Linux questo rende il fine riga visibile come un segno $), ma l'output inviato a stdout e stderr (sulle restanti due righe) è non reindirizzato . Modificare | cat -E in > out e, dopo aver eseguito lo script, osservare i contenuti del file out: gli ultimi due echo s non sono lì.

Ora modificare ls a foo (o qualche altro comando che non verrà trovato) ed eseguire di nuovo lo script. Questa volta l'output è:

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

e il file out ora ha i contenuti prodotti dalla prima linea echo.

Questo rende ciò che "fa veramente" exec "il più ovvio possibile (ma non più ovvio, come Albert Einstein non ha detto :-)).

Normalmente, quando la shell va ad eseguire un "comando semplice" (vedere la pagina di manuale per la definizione precisa, ma questo esclude specificamente i comandi in una "pipeline"), prepara qualsiasi operazione di reindirizzamento I/O specificata con <, > e così via aprendo i file necessari. Quindi la shell invoca fork (o una variante equivalente ma più efficiente come vfork o clone a seconda del sistema operativo, della configurazione, ecc.) E, nel processo figlio, riorganizza i descrittori di file aperti (utilizzando le chiamate dup2 o equivalenti) per ottenere il risultato accordi finali desiderate: > out muove il descrittore aperto a fd 1-stdout-mentre 6> out muove il descrittore aperto a fd 6.

Se si specifica l'exec parola chiave, però, il guscio sopprime il passo fork. Fa tutto il file di apertura e il file-descrittore-riorganizzare come al solito, ma questa volta, interessa tutti i successivi comandi. Infine, dopo aver eseguito tutti i reindirizzamenti, la shell tenta di execve() (nel senso di chiamata di sistema) il comando, se ce n'è uno.Se non è presente alcun comando o se la chiamata execve() non riesce e, la shell deve continuare a essere in esecuzione (è interattiva o è stato impostato execfail), i soldati della shell sono accesi. Se lo strumento execve() riesce, la shell non esiste più, essendo stata sostituita dal nuovo comando. Se execfail non è impostato e la shell non è interattiva, la shell viene chiusa.

(C'è anche la complicazione della funzione command_not_found_handle shell: bash di exec sembra di sopprimere eseguirlo, sulla base dei risultati dei test Il exec parola chiave, in generale, rende la shell non guardare le proprie funzioni, vale a dire, se si dispone di una. funzione di shell f, in esecuzione f come un semplice comando esegue la funzione di shell, come fa (f) che corre in un sub-shell, ma in esecuzione (exec f) salta su di esso.)


per quanto riguarda il motivo per cui ls>out1 ls>out2 crea due file (con o senza exec), questo è abbastanza semplice: la shell apre ogni reindirizzamento e quindi usa dup2 per spostare i descrittori di file. Se hai due ordinari reindirizzamenti >, la shell apre entrambi, sposta il primo su fd 1 (stdout), quindi sposta il secondo su fd 1 (stdout di nuovo), chiudendo il primo nel processo. Infine, viene eseguito ls ls, perché è ciò che rimane dopo aver rimosso il >out1 >out2. Finché non esiste un file denominato ls, il comando ls si lamenta di stderr e non scrive nulla su stdout.

+1

Per quanto riguarda la shell (non) interattiva, la manpage dice "Se' command' non può per qualche ragione, una shell non interattiva si chiude, a meno che l'opzione di shell 'execfail' sia abilitata, nel qual caso restituisce un errore.Una shell interattiva restituisce un errore" – user123444555621

+0

@torek guarda la parte EDIT nella domanda – artaxerxe

+0

@ Pumbaa80 : molto bella; questo aiuta a rendere "chiaro come funziona exec" più chiaro. Aggiungerò una nota alla mia risposta. – torek

Problemi correlati