eval
e exec
são animais completamente diferentes. (Além do fato de que ambos irão executar comandos, mas o mesmo acontece com tudo que você faz em um shell.)
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
O que exec cmd
faz é exatamente o mesmo que executar cmd
, exceto que o shell atual é substituído pelo comando, em vez de um processo separado sendo executado. Internamente, em execução, diga /bin/ls
chamará fork()
para criar um processo filho e, em seguida, exec()
no filho para executar /bin/ls
. exec /bin/ls
, por outro lado, não será bifurcado, mas apenas substitui o shell.
Compare:
$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo
com
$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217
echo $$
imprime o PID do shell que iniciei, e listar /proc/self
nos fornece o PID do ls
que foi executado a partir do shell. Geralmente, os IDs do processo são diferentes, mas com exec
o shell e ls
têm o mesmo ID do processo. Além disso, o comando seguinte exec
não foi executado, já que o shell foi substituído.
Por outro lado:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
eval
executará os argumentos como um comando no shell atual. Em outras palavras, eval foo bar
é o mesmo que foo bar
. Mas as variáveis serão expandidas antes de serem executadas, para que possamos executar os comandos salvos nas variáveis do shell:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
Ele não criará um processo filho, portanto, a variável é definida no shell atual. (É claro que eval /bin/ls
criará um processo filho, da mesma forma que um antigo /bin/ls
faria.)
Ou podemos ter um comando que produza comandos do shell. Executar ssh-agent
inicia o agente em segundo plano e gera várias atribuições de variáveis, que podem ser definidas no shell atual e usadas por processos filhos (os comandos ssh
que você executaria). Portanto, ssh-agent
pode ser iniciado com:
eval $(ssh-agent)
E o shell atual obterá as variáveis para outros comandos herdarem.
É claro que, se a variável cmd
contivesse algo como rm -rf $HOME
, a execução de eval "$cmd"
não seria algo que você gostaria de fazer. Mesmo coisas como as substituições de comandos dentro da string seriam processadas, então deve-se realmente ter certeza de que a entrada para eval
é segura antes de usá-la.
Geralmente, é possível evitar eval
e evitar misturar código e dados acidentalmente de maneira errada.