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.
Ho imparato qualcosa! – blueshift
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 –
@ J-16SDiZ corretto come suggerito, ma il problema persiste – artaxerxe