Qual è la differenza tra eval e exec?


Risposte:


124

evale execsono animali completamente diversi. (A parte il fatto che entrambi eseguiranno i comandi, ma anche tutto ciò che fai in una shell.)

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

Quello che exec cmdfa, è esattamente lo stesso che solo in esecuzione cmd, tranne per il fatto che la shell corrente viene sostituita con il comando, invece di eseguire un processo separato. Internamente, l'esecuzione di say /bin/lschiamerà fork()per creare un processo figlio e quindi exec()nel bambino eseguirà /bin/ls. exec /bin/lsd'altra parte non si biforcherà, ma sostituirà semplicemente la shell.

Confrontare:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

con

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$stampa il PID della shell che ho avviato e l'elenco /proc/selfci fornisce il PID della lsshell eseguita dalla shell. Di solito, gli ID processo sono diversi, ma con execla shell e lshanno lo stesso ID processo. Inoltre, il comando seguente execnon è stato eseguito, poiché la shell è stata sostituita.


D'altro canto:

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

evaleseguirà gli argomenti come comando nella shell corrente. In altre parole eval foo barè lo stesso di solo foo bar. Ma le variabili verranno espanse prima dell'esecuzione, quindi possiamo eseguire i comandi salvati nelle variabili shell:

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

Essa non crea un processo figlio, quindi la variabile è impostata nella shell corrente. (Naturalmente eval /bin/lscreerà un processo figlio, proprio come /bin/lsfarebbe un vecchio normale .)

Oppure potremmo avere un comando che emette comandi di shell. L'esecuzione ssh-agentavvia l'agente in background e genera una serie di assegnazioni di variabili, che possono essere impostate nella shell corrente e utilizzate dai processi figlio (i sshcomandi da eseguire). Quindi ssh-agentpuò essere avviato con:

eval $(ssh-agent)

E la shell corrente otterrà le variabili da ereditare per altri comandi.


Ovviamente, se la variabile cmdcontenesse qualcosa del genere rm -rf $HOME, l'esecuzione eval "$cmd"non sarebbe qualcosa che vorresti fare. Anche cose come le sostituzioni di comandi all'interno della stringa verrebbero elaborate, quindi si dovrebbe davvero essere sicuri che l'input to evalsia sicuro prima di usarlo.

Spesso è possibile evitare evaled evitare anche la miscelazione accidentale di codice e dati nel modo sbagliato.


Questa è un'ottima risposta, @ilkkachu. Grazie!
Willian Paixao,

Maggiori informazioni su l'uso di eval può essere trovato qui: stackoverflow.com/a/46944004/2079103
clearlight

1
@clearlight, beh, mi ricorda di aggiungere la solita dichiarazione di noneval responsabilità riguardo al non utilizzare in primo luogo anche questa risposta. Cose come la modifica indiretta delle variabili possono essere fatte in molte shell tramite declare/ typeset/ namerefe espansioni come ${!var}, quindi userei quelle invece che a evalmeno che non dovessi davvero evitarlo.
ilkkachu,

27

execnon crea un nuovo processo. Esso sostituisce il processo corrente con il nuovo comando. Se lo hai fatto sulla riga di comando, la sessione della shell terminerà effettivamente (e forse ti disconnetterai o chiuderai la finestra del terminale!)

per esempio

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

Eccomi qui ksh(il mio guscio normale). Inizio bashe poi dentro bash exec /bin/echo. Possiamo vedere che sono stato ricacciato in kshseguito perché il bashprocesso è stato sostituito da /bin/echo.


umm sulla faccia ricaduta nel processo ksh b / c è stato sostituito da echo non ha molto senso?

14

TL; DR

execviene utilizzato per sostituire l'attuale processo di shell con nuovi e gestire i reindirizzamenti / descrittori di file di flusso se non è stato specificato alcun comando. evalviene utilizzato per valutare le stringhe come comandi. Entrambi possono essere utilizzati per creare ed eseguire un comando con argomenti noti in fase di esecuzione, ma execsostituisce il processo della shell corrente oltre all'esecuzione dei comandi.

exec incorporato

Sintassi:

exec [-cl] [-a name] [command [arguments]]

Secondo il manuale se esiste un comando specificato questo incorporato

... sostituisce la shell. Non viene creato alcun nuovo processo. Gli argomenti diventano gli argomenti da comandare.

In altre parole, se si eseguiva bashPID 1234 e se si eseguisse exec top -u rootall'interno di quella shell, il topcomando avrà quindi PID 1234 e sostituirà il processo della shell.

Dove è utile? In qualcosa noto come script wrapper. Tali script costruiscono serie di argomenti o prendono determinate decisioni su quali variabili passare nell'ambiente, quindi usano execper sostituirsi con qualsiasi comando sia specificato e, naturalmente, forniscono quegli stessi argomenti che lo script wrapper ha costruito lungo la strada.

Ciò che il manuale afferma anche che:

Se il comando non viene specificato, tutti i reindirizzamenti avranno effetto nella shell corrente

Questo ci consente di reindirizzare qualsiasi cosa dagli attuali flussi di output delle shell in un file. Ciò può essere utile per scopi di registrazione o filtro, in cui non si desidera vedere i stdoutcomandi ma solo stderr. Ad esempio, in questo modo:

bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt 
2017 05 20 星期六 05:01:51 MDT
HELLO WORLD

Questo comportamento lo rende utile per accedere agli script della shell , reindirizzare i flussi a file o processi separati e altre cose divertenti con descrittori di file.

A livello di codice sorgente almeno per la bashversione 4.3, execè definito in builtins/exec.def. Analizza i comandi ricevuti e, se ce ne sono, passa le cose alla shell_execve()funzione definita nel execute_cmd.cfile.

Per farla breve, esiste una famiglia di execcomandi nel linguaggio di programmazione C ed shell_execve()è sostanzialmente una funzione wrapper di execve:

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval integrato

Il manuale di bash 4.3 afferma (enfasi aggiunta da me):

Gli arg vengono letti e concatenati insieme in un unico comando. Questo comando viene quindi letto ed eseguito dalla shell e il suo stato di uscita viene restituito come valore di eval.

Si noti che non si verificano sostituzioni di processi. A differenza di execdove l'obiettivo è simulare la execve()funzionalità, l' evalintegrato serve solo a "valutare" gli argomenti, proprio come se l'utente li avesse digitati sulla riga di comando. Pertanto, vengono creati nuovi processi.

Dove potrebbe essere utile? Come ha sottolineato Gilles in questa risposta , "... eval non viene usato molto spesso. In alcune shell, l'uso più comune è quello di ottenere il valore di una variabile il cui nome non è noto fino al runtime". Personalmente, l'ho usato in un paio di script su Ubuntu in cui era necessario eseguire / valutare un comando basato sullo spazio di lavoro specifico che l'utente stava attualmente utilizzando.

A livello di codice sorgente, viene definito builtins/eval.defe passa la stringa di input analizzata per evalstring()funzionare.

Tra le altre cose, evalpuò assegnare variabili che rimangono nell'attuale ambiente di esecuzione della shell, mentre execnon possono:

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found

@ ctrl-alt-delor Ho modificato quella parte, grazie, anche se probabilmente genera un nuovo processo, il PID rimane semplicemente lo stesso della shell corrente. In futuro prendi in considerazione la modifica delle risposte, invece di lasciare un commento e un voto negativo, in particolare quando è in questione una cosa minore come una frase di 7 parole; è molto meno tempo per modificare e correggere una risposta ed è molto più utile.
Sergiy Kolodyazhnyy

5
creando un nuovo processo figlio, esegui gli argomenti e restituisce lo stato di uscita.

Cosa? Il punto evalè che non crea in alcun modo un processo figlio. Se lo faccio

eval "cd /tmp"

in una shell, quindi la shell corrente avrà cambiato directory. Né execcrea un nuovo processo figlio, invece cambia l'eseguibile corrente (ovvero la shell) per quello dato; l'id di processo (e aprire file e altre cose) rimangono gli stessi. Al contrario eval, un execnon tornerà alla shell chiamante a meno che lo execstesso non fallisca a causa dell'incapacità di trovare o caricare l'eseguibile o morire per problemi di espansione dell'argomento.

evalfondamentalmente interpreta i suoi argomenti come una stringa dopo la concatenazione, ovvero farà un ulteriore livello di espansione con caratteri jolly e suddivisione degli argomenti. execnon fa niente del genere.


1
La domanda originale era "eval ed exec sono entrambi comandi integrati di Bash (1) ed esegue i comandi, creando un nuovo processo figlio, eseguendo gli argomenti e restituendo lo stato di uscita"; Ho eliminato la falsa presunzione.
Charles Stewart,

-1

Valutazione

Questi lavori:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

Tuttavia, questi non:

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

Sostituzione dell'immagine di processo

Questo esempio dimostra come execsostituisce l'immagine del suo processo di chiamata:

# Get PID of current shell
sh$ echo $$
1234

# Enter a subshell with PID 5678
sh$ sh

# Check PID of subshell
sh-subshell$ echo $$
5678

# Run exec
sh-subshell$ exec echo $$
5678

# We are back in our original shell!
sh$ echo $$
1234

Si noti che è stato exec echo $$eseguito con il PID della subshell! Inoltre, dopo che è stato completato, siamo tornati nella nostra sh$shell originale .

D'altra parte, evalnon senza sostituire l'immagine di processo. Piuttosto, esegue il comando dato come si farebbe normalmente all'interno della shell stessa. (Naturalmente, se si esegue un comando che richiede la generazione di un processo ... fa proprio questo!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678

Ho pubblicato questa risposta perché gli altri esempi non l'hanno davvero illustrata in modo sufficientemente comprensibile per menti deboli come I.
Mateen Ulhaq

Scarsa scelta delle parole: la sostituzione del processo è un concetto esistente che sembra non essere correlato a ciò che stai illustrando qui.
muru

@muru La "sostituzione di processo" è migliore? Non sono sicuro di quale sia la terminologia corretta. La manpage sembra chiamarla "sostituzione dell'immagine di processo".
Mateen Ulhaq,

Hmm, non lo so. (Ma quando sento "sostituendo l'immagine di processo", credo: exec)
Muru
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.