Atualização: algumas pessoas dizem que nunca se deve usar avaliação. Discordo. Eu acho que o risco surge quando uma entrada corrompida pode ser passada para eval
. No entanto, existem muitas situações comuns em que isso não representa um risco e, portanto, vale a pena saber como usar o eval em qualquer caso. Essa resposta do stackoverflow explica os riscos da avaliação e as alternativas à avaliação. Por fim, cabe ao usuário determinar se / quando a avaliação é segura e eficiente de usar.
A eval
instrução bash permite executar linhas de código calculadas ou adquiridas pelo seu script bash.
Talvez o exemplo mais direto seja um programa bash que abra outro script bash como um arquivo de texto, leia cada linha de texto e use eval
para executá-los em ordem. Esse é essencialmente o mesmo comportamento da source
instrução bash , que é o que se usaria, a menos que fosse necessário realizar algum tipo de transformação (por exemplo, filtragem ou substituição) no conteúdo do script importado.
Raramente precisei eval
, mas achei útil ler ou escrever variáveis cujos nomes estavam contidos em cadeias atribuídas a outras variáveis. Por exemplo, para executar ações em conjuntos de variáveis, mantendo o tamanho do código pequeno e evitando redundância.
eval
é conceitualmente simples. No entanto, a sintaxe estrita do idioma bash e a ordem de análise do intérprete do bash podem ser diferenciadas e eval
parecer enigmáticas e difíceis de usar ou entender. Aqui estão os itens essenciais:
O argumento passado para eval
é uma expressão de cadeia que é calculada em tempo de execução. eval
executará o resultado final analisado de seu argumento como uma linha de código real em seu script.
A sintaxe e a ordem de análise são rigorosas. Se o resultado não for uma linha executável de código bash, no escopo do seu script, o programa falhará na eval
instrução ao tentar executar o lixo.
Ao testar, você pode substituir a eval
instrução echo
e ver o que é exibido. Se for um código legítimo no contexto atual, sua execução eval
funcionará.
Os exemplos a seguir podem ajudar a esclarecer como o eval funciona ...
Exemplo 1:
eval
A declaração na frente do código 'normal' é um NOP
$ eval a=b
$ eval echo $a
b
No exemplo acima, as primeiras eval
instruções não têm finalidade e podem ser eliminadas. eval
é inútil na primeira linha porque não há aspecto dinâmico no código, ou seja, ele já foi analisado nas linhas finais do código bash, portanto, seria idêntico como uma declaração normal de código no script bash. O segundo também eval
é inútil, porque, embora exista uma etapa de análise convertida $a
para o seu equivalente literal de cadeia de caracteres, não há indireção (por exemplo, nenhuma referência via valor da cadeia de um substantivo bash real ou variável de script realizada por bash), portanto se comportaria de forma idêntica como uma linha de código sem o eval
prefixo.
Exemplo 2:
Execute a atribuição de var usando nomes de var passados como valores de sequência.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Se você fosse echo $key=$val
, a saída seria:
mykey=myval
Que , sendo o resultado final da análise de cadeia, é o que será executado por eval, portanto, o resultado da instrução echo no final ...
Exemplo 3:
Incluindo mais Indireção no Exemplo 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
O exposto acima é um pouco mais complicado que o exemplo anterior, confiando mais na ordem de análise e nas peculiaridades do bash. A eval
linha seria aproximadamente analisada internamente na seguinte ordem (observe que as seguintes instruções são pseudocódigo, não código real, apenas para tentar mostrar como a instrução seria dividida em etapas internamente para chegar ao resultado final) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Se a ordem de análise assumida não explica o que a avaliação está fazendo o suficiente, o terceiro exemplo pode descrever a análise com mais detalhes para ajudar a esclarecer o que está acontecendo.
Exemplo 4:
Descubra se os vars, cujos nomes estão contidos em cadeias, eles próprios contêm valores de cadeia.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
Na primeira iteração:
varname="myvarname_a"
Bash analisa o argumento eval
e eval
vê literalmente isso em tempo de execução:
eval varval=\$$myvarname_a
O pseudocódigo a seguir tenta ilustrar como o bash interpreta a linha de código real acima , para chegar ao valor final executado por eval
. (as seguintes linhas descritivas, não o código bash exato):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Depois que toda a análise é feita, o resultado é o que é executado, e seu efeito é óbvio, demonstrando que não há nada particularmente misterioso sobre eval
si mesmo, e a complexidade está na análise de seu argumento.
varval="User-provided"
O código restante no exemplo acima simplesmente testa para verificar se o valor atribuído a $ varval é nulo e, nesse caso, solicita que o usuário forneça um valor.
$($n)
é executado$n
em um subshell. Ele tenta executar o comando1
que não existe.