evale execsono entrambi integrati nei comandi di bash (1) che eseguono i comandi.
Vedo anche execalcune opzioni, ma è l'unica differenza? Cosa succede al loro contesto?
evale execsono entrambi integrati nei comandi di bash (1) che eseguono i comandi.
Vedo anche execalcune opzioni, ma è l'unica differenza? Cosa succede al loro contesto?
Risposte:
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.
eval 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.
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.
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.
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;
{
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
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.
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
exec)