Bisogno di spiegazioni per Linux bash builtin comando exec comportamento

Da Bash Manuale Di Riferimento ho la seguente su exec bash comando incorporato:

Se il comando è fornito, sostituisce la shell senza la creazione di un nuovo processo.

Ora ho il seguente bash script:

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

Questo 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 output:

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

La mia domanda è:

Se quando exec viene richiamato sostituisce la shell senza la creazione di un nuovo processo di, perché quando mettere | cat, il echo 123 è stampato, ma senza di essa, non è così. Così, sarei felice se qualcuno può spiegare che cosa è la logica di questo comportamento.

Grazie.

MODIFICA:
Dopo la @torek risposta, ho un’ancora di più per spiegare il comportamento:

1.exec ls>out comando crea il out file e mettete il ls‘s il risultato di un comando;

2.exec ls>out1 ls>out2 crea solo il file, ma non la mettete all’interno di ogni risultato. Se il comando funziona come suggerito, penso che il comando numero 2 dovrebbe avere lo stesso risultato del comando numero 1 (anche di più, penso che non dovrebbe avere aveva creato il out2 file).

  • Ho imparato qualcosa!
  • Si collega su exec è il C exec funzione. Quello che si sta testando è la bash exec condominio. Non sono la stessa cosa. Vedere www.gnu.org/software/bash/manual/bashref.html
  • corretto come hai suggerito, ma il problema persiste
InformationsquelleAutor artaxerxe | 2012-03-29

 

One Reply
  1. 39

    In questo particolare caso, si ha la exec in una pipeline. In ordine di esecuzione di una serie di pipeline di comandi in shell deve inizialmente forcella, facendo una sub-shell. (In particolare si deve creare il tubo, poi forcella, in modo che il tutto eseguito “a sinistra” del tubo può avere il suo output inviato a tutto ciò che è “di destra” del tubo.)

    Per vedere che questo è in realtà ciò che sta accadendo, di confrontare:

    { ls; echo this too; } | cat

    con:

    { exec ls; echo this too; } | cat

    Ex corre ls senza lasciare il sub-shell, in modo che questo sub-shell è quindi ancora in giro per eseguire il echo. Quest’ultima si estende ls lasciando il sub-shell, che quindi non è più lì a fare il echo, e this too non viene stampato.

    (L’uso di ricci-graffe { cmd1; cmd2; } normalmente sopprime il sub-shell fork azione che si ottiene con le parentesi (cmd1; cmd2), ma nel caso di un tubo, la forcella è “costretto”, per così dire.)

    Reindirizzamento della shell corrente avviene solo se c’è “niente di esecuzione”, per così dire, dopo la parola exec. Così, ad esempio, exec >stdout 4<input 5>>append modifica di shell corrente, ma exec foo >stdout 4<input 5>>append cerca di comando exec foo. [Nota: questo non è esatto; vedi addendum.]

    È interessante notare che, in una shell interattiva, dopo exec foo >output non riesce perché non c’è nessun comando foo, il guscio si attacca in giro, ma stdout rimane reindirizzato a file output. (Si può recuperare con exec >/dev/tty. In uno script, il mancato exec foo termina lo script.)


    Con una punta del cappello a @Pumbaa80, qui c’è qualcosa di ancora più eloquenti:

    #! /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 giù dal mio solito cat -vET, che è il mio pratico go-to per “fammi vedere i caratteri non stampabili in un modo riconoscibile”). Quando viene eseguito questo script, l’uscita dal ls ha cat -E applicata (su Linux, questo rende end-of-line visibile come un segno di$), ma l’output inviato a stdout e stderr (le altre due linee) è non reindirizzato. Modificare il | cat -E per > out e, dopo l’esecuzione dello script, osservare il contenuto del file out: la finale di due echos non c’.

    Ora di cambiare il ls per foo (o qualche altro comando che non verrà trovato) ed eseguire nuovamente lo script. Questa volta il risultato è:

    $ ./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 echo linea.

    Questo rende la cosa exec “davvero”, come ovvio e come è possibile (ma non di più ovvio, come Albert Einstein non metterlo 🙂 ).

    Normalmente, quando la shell va a eseguire un “semplice comando” (vedi la pagina di manuale per la definizione precisa, ma questo esclude espressamente i comandi in una “pipeline”), si prepara qualsiasi reindirizzamento I/O operazioni di cui con <, >, con l’apertura dei file necessari. Quindi la shell richiama fork (o un equivalente, ma più efficiente variante come vfork o clone a seconda sottostante sistema operativo, configurazione, ecc), e, il processo figlio, riorganizza i descrittori di file aperti (utilizzando dup2 chiamate o equivalente) per ottenere il desiderato disposizioni finali: > out si muove aperto descrittore di fd 1—stdout—mentre 6> out si muove aperto descrittore di fd 6.

    Se si specificare il exec parola chiave, però, la shell sopprime il fork passo. Non tutti l’apertura del file e file descriptor-riorganizzare, come al solito, ma questa volta, colpisce qualsiasi e tutti i successivi comandi. Infine, dopo aver fatto tutte le redirezioni, la shell tenta di execve() (nel sistema di chiamata del senso) il comando, se c’è. Se non vi è alcun comando, o se il execve() chiamata non riesce e la shell dovrebbe continuare a correre (è interattivo o hai impostato execfail), la shell di soldati. Se il execve() ha esito positivo, la shell non esiste più, essendo stato sostituito dal nuovo comando. Se execfail viene annullata e la shell non interattiva, esce dalla shell.

    (C’è anche la complicazione aggiunta di command_not_found_handle funzione di shell: bash exec sembra sopprimere l’esecuzione, in base ai risultati dei test. Il exec parola chiave, in generale, rende la shell di non guardare le proprie funzioni, ovvero, 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 l’esecuzione di (exec f) salta su di esso.)


    Perché questo ls>out1 ls>out2 crea due file (con o senza un exec), questo è abbastanza semplice: il guscio si apre ogni reindirizzamento, e quindi utilizza dup2 per spostare i descrittori di file. Se si dispone di due comuni > redirect, il guscio si apre sia, si muove il primo a fd 1 (stdout), poi si sposta il secondo fd 1 (stdout di nuovo), chiudendo il primo nel processo. Infine, corre ls ls, perché questo è ciò che rimane dopo la rimozione del >out1 >out2. Fintanto che non vi è alcun file di nome ls, il ls comando si lamenta per stderr, e scrive nulla su stdout.

    • Come per il (non)shell interattiva, il manuale dice ” Se command non può essere eseguito per qualche motivo, una shell non interattiva esce, a meno che l’opzione di shell execfail è attivata, nel qual caso restituisce errore. Una shell interattiva restituisce l’errore”
    • guardate la parte di MODIFICA in questione
    • molto bello; questo contribuisce a rendere “come exec opere” più chiara. Vorrei aggiungere una nota alla mia risposta.

Lascia un commento