evale execsão ambos comandos internos do bash (1) que executam comandos.
Também vejo execalgumas opções, mas essa é a única diferença? O que acontece com o contexto deles?
evale execsão ambos comandos internos do bash (1) que executam comandos.
Também vejo execalgumas opções, mas essa é a única diferença? O que acontece com o contexto deles?
Respostas:
evale execsão bestas completamente diferentes. (Além do fato de que ambos executarão comandos, tudo o que você faz em um shell também).
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
O que exec cmdfaz é exatamente o mesmo que apenas executar cmd, exceto que o shell atual é substituído pelo comando, em vez de um processo separado estar sendo executado. Internamente, executar o say /bin/lschamará fork()para criar um processo filho e, exec()em seguida, executar o filho /bin/ls. exec /bin/lspor outro lado, não bifurca, mas apenas substitui a concha.
Comparar:
$ 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 a listagem /proc/selfnos fornece o PID do lsque foi executado a partir do shell. Geralmente, os IDs de processo são diferentes, mas com execo shell e lstêm o mesmo ID de processo. Além disso, o comando a seguir execnão foi executado, pois o shell foi substituído.
Por outro lado:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
evalexecutará os argumentos como um comando no shell atual. Em outras palavras, eval foo baré o mesmo que justo foo bar. Mas as variáveis serão expandidas antes da execução, para que possamos executar comandos salvos nas variáveis do shell:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
Como não criará um processo filho, a variável é definida no shell atual. (É claro eval /bin/lsque criará um processo filho, da mesma maneira que um velho comum /bin/lsfaria.)
Ou podemos ter um comando que produz comandos shell. A execução ssh-agentinicia o agente em segundo plano e gera várias atribuições de variáveis, que podem ser definidas no shell atual e usadas pelos processos filhos (os sshcomandos que você executaria). Portanto, ssh-agentpode ser iniciado com:
eval $(ssh-agent)
E o shell atual obterá as variáveis para outros comandos herdarem.
Obviamente, se a variável cmdcontiver algo como rm -rf $HOME, executar eval "$cmd"não seria algo que você gostaria de fazer. Mesmo coisas como substituições de comando dentro da corda seria processado, por isso deve-se realmente ter certeza de que a entrada para evalé seguro antes de o utilizar.
Muitas vezes, é possível evitar evale evitar a mistura acidental de código e dados da maneira errada.
evalem primeiro lugar a esta resposta também. Coisas como variáveis de modificação indireta podem ser feitas em muitos shells através de declare/ typeset/ namerefe expansões como ${!var}, então eu os usaria em vez de, a evalmenos que realmente tivesse que evitá-lo.
execnão cria um novo processo. Ele substitui o processo atual pelo novo comando. Se você fez isso na linha de comando, ele efetivamente encerrará sua sessão do shell (e talvez você faça logout ou feche a janela do terminal!)
por exemplo
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
Aqui estou ksh(minha concha normal). Eu começo bashe depois dentro do bash exec /bin/echo. Podemos ver que fui devolvido kshdepois porque o bashprocesso foi substituído por /bin/echo.
execé usado para substituir o processo atual do shell por novos e manipular descritores de redirecionamento / arquivo de fluxo, se nenhum comando foi especificado. evalé usado para avaliar seqüências de caracteres como comandos. Ambos podem ser usados para criar e executar um comando com argumentos conhecidos em tempo de execução, mas execsubstituem o processo do shell atual, além de executar comandos.
Sintaxe:
exec [-cl] [-a name] [command [arguments]]
De acordo com o manual, se houver um comando especificado, este built-in
... substitui o shell. Nenhum novo processo é criado. Os argumentos se tornam os argumentos a serem comandados.
Em outras palavras, se você estava executando bashcom o PID 1234 e se executava exec top -u rootnesse shell, o topcomando terá o PID 1234 e substituirá o processo do shell.
Onde isso é útil? Em algo conhecido como scripts de wrapper. Esses scripts constroem conjuntos de argumentos ou tomam certas decisões sobre quais variáveis passar para o ambiente e, em seguida, usam-se execpara substituir a si mesmo por qualquer comando especificado e, é claro, fornecendo os mesmos argumentos que o script do wrapper criou ao longo do caminho.
O que o manual também afirma é que:
Se o comando não for especificado, qualquer redirecionamento entrará em vigor no shell atual
Isso nos permite redirecionar qualquer coisa dos fluxos de saída dos shells atuais para um arquivo. Isso pode ser útil para fins de registro ou filtragem, onde você não deseja ver stdoutapenas os comandos stderr. Por exemplo, assim:
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
Esse comportamento o torna útil para fazer logon em scripts de shell , redirecionar fluxos para separar arquivos ou processos e outras coisas divertidas com descritores de arquivo.
No nível do código-fonte, pelo menos para a bashversão 4.3, o execinterno é definido em builtins/exec.def. Ele analisa os comandos recebidos e, se houver algum, passa as coisas para a shell_execve()função definida no execute_cmd.carquivo.
Para encurtar a história, existe uma família de execcomandos na linguagem de programação C e shell_execve()é basicamente uma função de wrapper de execve:
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
O manual do bash 4.3 afirma (ênfase adicionada por mim):
Os argumentos são lidos e concatenados juntos em um único comando. Este comando é então lido e executado pelo shell e seu status de saída é retornado como o valor de eval.
Observe que não há substituição do processo. Diferentemente de execonde o objetivo é simular a execve()funcionalidade, o evalbuilt-in serve apenas para "avaliar" argumentos, como se o usuário os tivesse digitado na linha de comando. Como tal, novos processos são criados.
Onde isso pode ser útil? Como Gilles apontou nesta resposta , "... eval não é usado com muita frequência. Em algumas conchas, o uso mais comum é obter o valor de uma variável cujo nome não é conhecido até o tempo de execução". Pessoalmente, eu o usei em alguns scripts no Ubuntu, onde era necessário executar / avaliar um comando com base no espaço de trabalho específico que o usuário estava usando no momento.
No nível do código-fonte, ele é definido builtins/eval.defe passa a sequência de entrada analisada para evalstring()funcionar.
Entre outras coisas, evalpode atribuir variáveis que permanecem no ambiente de execução de shell atual, enquanto execnão pode:
$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
criando um novo processo filho, execute os argumentos e retorne o status de saída.
Uh o quê? O ponto principal evalé que ele não cria de forma alguma um processo filho. Se eu fizer
eval "cd /tmp"
em um shell, depois o shell atual terá mudado de diretório. Nem execcria um novo processo filho; em vez disso, altera o executável atual (ou seja, o shell) para o determinado; a identificação do processo (e arquivos abertos e outras coisas) permanecem os mesmos. Ao contrário eval, um execnão retornará ao shell de chamada, a menos que o execpróprio falhe devido à incapacidade de encontrar ou carregar o executável ou morrer devido a problemas de expansão de argumento.
evalbasicamente interpreta seus argumentos como uma sequência após concatenação, ou seja, ele fará uma camada extra de expansão de curinga e divisão de argumentos. execnão faz nada assim.
Avaliação
Estes trabalhos:
$ echo hi
hi
$ eval "echo hi"
hi
$ exec echo hi
hi
No entanto, estes não:
$ exec "echo hi"
bash: exec: echo hi: not found
$ "echo hi"
bash: echo hi: command not found
Substituição da imagem do processo
Este exemplo demonstra como execsubstitui a imagem do seu processo de chamada:
# 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
Observe que foi exec echo $$executado com o PID do subshell! Além disso, depois de concluído, estávamos de volta ao nosso sh$shell original .
Por outro lado, evalse não substituir a imagem do processo. Em vez disso, ele executa o comando fornecido, como faria normalmente dentro do próprio shell. (Obviamente, se você executar um comando que exige que um processo seja gerado ... isso é exatamente o que acontece!)
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)