O que é o comando "eval" no bash?

176

O que você pode fazer com o evalcomando? Por que isso é útil? É algum tipo de função interna no bash? Não existe uma manpágina para isso ..

LanceBaynes
fonte
28
Use type commandpara saber de que tipo é um comando . ( type evalneste caso)
rozcietrzewiacz 22/10
9
"eval é um shell embutido"
Nuno Rafael Figueiredo
3
help evalpara obter página "man" do seu shell
m-ric
eval é um bash-builtin e está documentado na página de manual do bash. Então, basta digitar "man bash" e procurar a seção apropriada para eval. Isso também se aplica a outros bash-builtins.
Dirk Thannhäuser

Respostas:

132

evalfaz parte do POSIX. É uma interface que pode ser um shell embutido.

Está descrito no "Manual do Programador POSIX": http://www.unix.com/man-page/posix/1posix/eval/

eval - construct command by concatenating arguments

Vai pegar um argumento e construir um comando dele, que será executado pelo shell. Este é o exemplo da página de manual:

1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
  1. Na primeira linha, você define $foocom o valor '10'e $xcom o valor 'foo'.
  2. Agora defina $y, que consiste na string '$foo'. O cifrão deve ser escapado com '$'.
  3. Para verificar o resultado echo $y,.
  4. O resultado será a string '$foo'
  5. Agora repetimos a tarefa com eval. Ele primeiro avaliará $xa string 'foo'. Agora, temos a declaração y=$fooque será avaliada y=10.
  6. O resultado de echo $yagora é o valor '10'.

Essa é uma função comum em muitos idiomas, por exemplo, Perl e JavaScript. Veja perldoc eval para mais exemplos: http://perldoc.perl.org/functions/eval.html

eco
fonte
4
Na terminologia do shell, evalé um recurso interno, não uma função. Na prática, os built-ins se comportam muito como funções que não têm uma definição na linguagem, mas não tanto (como fica aparente se você tiver distorção suficiente para definir uma função chamada eval).
Gilles
thx, corrigiu isso :-)
echox 25/10
4
Qual é a diferença entre eval e backticks `?
JohnyTex
5
Backticks são atalhos para executar outro shell inteiro, o que significa que você terá outro processo filho bash / sh em execução no seu script.
Aaron R.
Por que eco $ y (estágio 3) traz de volta foo (10) em vez de $ foo? (ou seja, apenas o valor de foo em vez da string $ + o valor de foo)?
JohnDoea
60

Sim, evalé um comando interno do bash, portanto, é descrito na bashpágina de manual.

eval [arg ...]
    The  args  are read and concatenated together into a single com-
    mand.  This command is then read and executed by the shell,  and
    its  exit status is returned as the value of eval.  If there are
    no args, or only null arguments, eval returns 0.

Geralmente é usado em combinação com uma substituição de comando . Sem um explícito eval, o shell tenta executar o resultado de uma substituição de comando, não para avaliá- lo.

Diga que deseja codificar um equivalente a VAR=value; echo $VAR. Observe a diferença de como o shell lida com os escritos de echo VAR=value:

  1. andcoz@...:~> $( echo VAR=value )
    bash: VAR=value: command not found
    andcoz@...:~> echo $VAR
    <empty line>

    O shell tenta executar echoe VAR=valuecomo dois comandos separados. Emite um erro sobre a segunda string. A atribuição permanece ineficaz.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    O shell mescla (concatena) as duas seqüências echoe VAR=valueanalisa essa unidade única de acordo com as regras apropriadas e a executa.

Por último, mas não menos importante, evalpode ser um comando muito perigoso. Qualquer entrada em um evalcomando deve ser cuidadosamente verificada para evitar problemas de segurança.

andcoz
fonte
18

evalnão possui uma página de manual porque não é um comando externo separado, mas um shell interno, o que significa um comando interno e conhecido apenas pelo shell ( bash). A parte relevante da bashpágina de manual diz:

eval [arg ...]
    The args are read and concatenated together into a single command.  
    This command is then  read  and executed by the shell, and its exit 
    status is returned as the value of eval.  If there are no args, or only 
    null arguments, eval returns 0

Além disso, a saída se help evalé:

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

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.

evalé um comando poderoso e, se você pretende usá-lo, deve ter muito cuidado para evitar os possíveis riscos de segurança que o acompanham.

jw013
fonte
16

A instrução eval diz ao shell para pegar os argumentos de eval como comando e executá-los na linha de comando. É útil em uma situação como abaixo:

No seu script, se você estiver definindo um comando em uma variável e, posteriormente, desejar usar esse comando, use eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
Nikhil Gupta
fonte
3
O comentário na linha 4 do seu exemplo está errado: O comando acima não funcionou porque o bash tentou encontrar um comando chamado ls | more. Em outras palavras: O nome do comando único consistia em nove caracteres, incluindo os espaços e o símbolo de barra vertical .
TPI
Eu recebo exatamente o mesmo comportamento que ele relatou. bash --version == GNU bash, versão 3.2.57 (1) -release (x86_64-apple-darwin18)
Alexander Bird
10

O que é eval?

eval é um comando shell que geralmente é implementado como um componente interno.

No POSIX, ele é listado como parte de "2.14. Utilitários embutidos especiais" na entrada "eval" .
O que significa builtin é:

O termo "interno" implica que o shell pode executar o utilitário diretamente e não precisa procurá-lo.

O que isso faz?

Em termos simples: faz com que uma linha de entrada seja analisada duas vezes .

Como isso acontece?

O shell possui uma sequência de etapas a seguir para "processar" uma linha. Você pode olhar para esta imagem e perceber que eval é a única linha que sobe de volta à etapa 1, à esquerda. Na descrição do POSIX :

2.1 Introdução ao Shell

  1. O shell lê sua entrada ....
  2. O shell divide a entrada em tokens: palavras e operadores
  3. O shell analisa a entrada em comandos simples e compostos.
  4. O shell realiza várias expansões (separadamente) ...
  5. O shell executa o redirecionamento e remove os operadores de redirecionamento e seus operandos da lista de parâmetros.
  6. O shell executa uma função, arquivo executável interno ou script ...
  7. Opcionalmente, o shell aguarda a conclusão do comando e coleta o status de saída.

Na etapa 6, um built-in será executado.
Na etapa 6, eval faz com que a linha processada seja enviada de volta para a etapa 1.
É a única condição sob a qual a sequência de execução retorna.

É por isso que digo: Com eval, uma linha de entrada é analisada duas vezes .

Efeitos da análise duas vezes.

O primeiro.

E efeito mais importante para entender. É que uma consequência da primeira vez que uma linha está sujeita às sete passos shell mostrados acima, está citando . Na etapa 4 (expansões), também há uma sequência de etapas para executar todas as expansões , a última das quais é Remoção de cotação :

A remoção da cotação deve sempre ser realizada por último.

Portanto, sempre, há um nível de cotação removido.

Segundo.

Como conseqüência desse primeiro efeito, partes adicionais / diferentes da linha ficam expostas à análise de shell e a todas as outras etapas.

Exemplo.

Indirection.

Isso permite executar expansões indiretas:

a=b b=c    ;    eval echo \$$a            ### shall produce "c"

Por quê? Porque no primeiro loop, o primeiro $é citado.
Como tal, é ignorado para expansões pelo shell.
O próximo $com o nome a é expandido para produzir "b".
Em seguida, um nível de citação é removido, tornando o primeiro $não citado.
Fim do primeiro loop.

É então, no segundo loop, que a string $bé lida pelo shell.
Então expandido para "c"
E dado como argumento para echo.

Para "ver" o que o eval produzirá no primeiro loop (para ser avaliado novamente), use echo. Ou qualquer comando / script / programa que mostre claramente os argumentos:

$ a=b b=c
$ eval echo \$$a;
c

Substitua eval por eco para "ver" o que está acontecendo:

$ echo echo \$$a
echo $b

Também é possível mostrar todas as "partes" de uma linha com:

$ printf '<%s> ' echo \$$a
<echo> <$b>

Que, neste exemplo, é apenas um eco e uma variável, mas lembre-se de ajudar na avaliação de casos mais complexos.

Uma correção

Deve-se dizer que: há um erro no código acima, você vê?
Fácil: faltam algumas aspas.

Quão? você pode perguntar. Simples, vamos mudar as variáveis ​​(não o código):

$ a=b b="hi     jk"
$ eval echo \$$a
hi jk

Veja os espaços que faltam?
Isso ocorre porque o valor interno $bfoi dividido pelo shell.

Se isso não o convencer, tente o seguinte:

$ a=b b="hi  *  jk"
$ eval echo \$$a              ### warning this will expand to the list  
                              ### of all files in the present directory.

Por quê?

Citações ausentes. Para fazê-lo funcionar corretamente (adicione aspas internas "$a"e externas \").
Tente isto (é perfeitamente seguro):

$ a=b b="hi      *       jk"
$ eval echo \" \$"$a" \"
hi      *       jk

Sobre o manual:

Não existe uma página de manual para isso ..

Não, não há uma página de manual independente para isso. A pesquisa de manual com man -f evalou mesmo apropos evalnão mostra entrada.

Está incluído no interior man bash. Como é qualquer embutido.
Procure por "SHELL BUILTIN COMMANDS" e depois por "eval".

Uma maneira mais fácil de obter ajuda é: No bash, você pode fazer help evalpara ver a ajuda do built-in.

Por que eval é chamado de mal?

Porque está vinculando o texto ao código dinamicamente.

Em outras palavras: converte a lista de seus argumentos (e / ou expansões de tais argumentos) em uma linha executada. Se, por qualquer motivo, um argumento tiver sido definido por um invasor, você estará executando o código do invasor.

Ou ainda mais simples: com eval, você está dizendo a quem definiu o valor de um ou vários argumentos:

Vamos, sente-se aqui e digite qualquer linha de comando, eu irei executá-lo com meus poderes.

Isso é perigoso? Deve ser claro para todos que é.

A regra de segurança para eval deve ser:
Execute eval apenas nas variáveis ​​às quais você deu valor.

Leia mais detalhes aqui .


fonte
10

evalé uma característica das línguas mais interpretadas ( TCL, python, ruby...), não apenas conchas. É usado para avaliar o código dinamicamente.

Em shells, é implementado como um comando embutido no shell.

Basicamente, evalpega uma string como argumento e avalia / interpreta o código nela. Em shells, evalpode levar mais de um argumento, mas evalapenas concatena aqueles para formar a string a ser avaliada.

É muito poderoso porque você pode construir código dinamicamente e executá-lo, algo que não pode ser feito em linguagens compiladas como C.

Gostar:

varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
                           # which in Bourne-like shell language
                           # is a variable assignment.

Mas também é perigoso, pois é importante higienizar as partes dinâmicas (fornecidas externamente) do que é passado evalpelo mesmo motivo que é interpretado como código do shell.

Por exemplo, acima de se $1for evil-command; var, evalacabaria avaliando o evil-command; var=$varvaluecódigo do shell e o executaria evil-command.

A maldade de evalmuitas vezes é exagerada.

OK, é perigoso, mas pelo menos sabemos que é perigoso.

Um monte de outros comandos irá avaliar o código shell em seus argumentos, se não higienizado, como (dependendo do shell), [aka test, export, printf, GNU sed, awke, claro, sh/ bash/ perle todos os intérpretes ...

Exemplos (aqui usando unamecomo os evil-commande $acomo dados não autorizados fornecidos externamente):

$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux

$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"

Linux
$ a=';uname'; sh -c "echo $a"

Linux

Aqueles sed, export... comandos poderia ser considerado mais perigoso porque enquanto é óbvio eval "$var"fará com que o conteúdo $vara ser avaliado como código shell, não é tão óbvio com sed "s/foo/$var/"ou export "$var=value"ou [ "$var" -gt 0 ]. A perigosidade é a mesma, mas está oculta nesses outros comandos.

Stéphane Chazelas
fonte
O @BinaryZebra, sedcomo evalestá sendo passado uma string contida em uma variável, e para ambos, o conteúdo dessa string acaba sendo avaliado como código shell, então sedé tão perigoso quanto eval, é o que estou dizendo. sedestá passando uma string contendo uname(uname ainda não foi executado) e, através da invocação de sed, o comando uname acaba sendo executado. Como para avaliação. Em sed 's/foo/$a/g', você não está transmitindo dados não autorizados sed, não é disso que estamos falando aqui.
Stéphane Chazelas
2

Este exemplo pode lançar alguma luz:

#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"

Saída do script acima:

$VAR2
$VAR1
25
LoMaPh
fonte
Obrigado pela sua contribuição, mas, por mais divertidos que sejam, não estamos realmente interessados ​​em compilar uma lista de exemplos (um pouco diferentes) de uso eval. Você acredita que há algum aspecto fundamental e fundamental da funcionalidade evalque não é abordado nas respostas existentes? Nesse caso, explique -o e use o exemplo para ilustrar sua explicação. Por favor, não responda nos comentários; edite  sua resposta para torná-la mais clara e completa.
Scott Scott