O que é o comando “eval” no bash?

140

O que você pode fazer com o comando eval ? Por que isso é útil? É algum tipo de função embutida no bash? Não há man página para isso ..

    
por LanceBaynes 22.10.2011 / 22:02

7 respostas

111

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

É descrito no "Manual do Programador POSIX": link

eval - construct command by concatenating arguments

Ele pegará um argumento e construirá um comando, 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 $foo com o valor '10' e $x com o valor 'foo' .
  2. Agora defina $y , que consiste na string '$foo' . O sinal de dólar 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 . Primeiramente, ele avaliará $x na string 'foo' . Agora temos a declaração y=$foo , que será avaliada como y=10 .
  6. O resultado de echo $y agora é o valor '10' .

Esta é uma função comum em muitos idiomas, por ex. Perl e JavaScript. Dê uma olhada no perldoc eval para mais exemplos: link

    
por 22.10.2011 / 22:37
54

Sim, eval é um comando interno bash, portanto, é descrito na página bash man.

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.

Normalmente, ele é usado em combinação com uma Substituição de Comando . Sem um eval explícito, o shell tenta executar o resultado de uma substituição de comando, não para avaliar ele.

Digamos que você queira codificar um equivalente a VAR=value; echo $VAR . Observe a diferença em 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 echo e VAR=value como dois comandos separados. Ele lança um erro sobre a segunda string. A tarefa continua ineficaz.

  2. andcoz@...:~> eval $( echo VAR=value )
    andcoz@...:~> echo $VAR
    value
    
    O shell mescla (concatena) as duas strings echo e VAR=value , analisa essa unidade única de acordo com as regras apropriadas e a executa.

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

    
por 22.10.2011 / 22:36
16

eval não possui página man porque não é um comando externo separado, mas um shell embutido, significando um comando interno e conhecido apenas pelo shell ( bash ). A parte relevante da página bash man 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 for:

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 possíveis riscos de segurança que vem com isso.

    
por 22.10.2011 / 22:35
15

A declaração eval diz ao shell para usar os argumentos do eval como comando e executá-los por meio da linha de comando. É útil em uma situação como abaixo:

Em seu script, se você estiver definindo um comando em uma variável e, posteriormente, quiser 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 >
    
por 23.09.2014 / 08:21
9

eval é um recurso da maioria dos idiomas interpretados ( TCL , python , ruby ...) e não apenas shells. É usado para avaliar o código dinamicamente.

Em shells, ele é implementado como um comando interno do shell.

Basicamente, eval toma uma string como argumento e avalia / interpreta o código nela. Em shells, eval pode receber mais de um argumento, mas eval apenas concatena esses para formar a string a ser avaliada.

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

Como:

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) daquilo que é passado para eval , porque é interpretado como código de shell.

Por exemplo, acima se $1 for evil-command; var , eval concluirá a avaliação do código shell evil-command; var=$varvalue e, em seguida, executará esse evil-command .

A maldade de eval é muitas vezes exagerada.

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

Muitos outros comandos avaliarão o código do shell em seus argumentos se não forem limpos, como (dependendo do shell), [ aka test , export , printf , GNU sed , awk e, claro, sh / bash / perl e todos os intérpretes ...

Exemplos (usando aqui uname como evil-command e $a como dados não-analisados 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

Esses comandos sed , export ... podem ser considerados mais perigosos porque, embora seja óbvio, eval "$var" fará com que o conteúdo de $var seja 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á escondida nesses outros comandos.

    
por 18.12.2015 / 11:49
8

O que é eval?

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

No POSIX ele é listado como parte de "2.14. Utilitários Internos Especiais" na entrada "eval" .
O que significa embutir é:

The term "built-in" implies that the shell can execute the utility directly and does not need to search for it.

O que isso faz?

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

Como isso acontece?

O shell tem uma sequência de etapas que segue para "processar" uma linha. Você poderia olhar para esta imagem e perceber que eval é a única linha que sobe, de volta ao passo 1, à esquerda. De a descrição POSIX :

2.1 Shell Introduction

  1. The shell reads its input ....
  2. The shell breaks the input into tokens: words and operators
  3. The shell parses the input into simple and compound commands.
  4. The shell performs various expansions (separately) ...
  5. The shell performs redirection and removes redirection operators and their operands from the parameter list.
  6. The shell executes a function, built-in, executable file, or script ...
  7. The shell optionally waits for the command to complete and collects the exit status.

No passo 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 seqüência de execução retorna.

That is why I say: With eval an input line is parsed twice.

Efeitos da análise duas vezes.

O primeiro.

E efeito mais importante para entender. Essa é uma consequência da primeira vez que uma linha está sujeita às sete etapas de shell mostradas acima, é citando . Na etapa 4 (expansões), há também uma sequência de etapas para realizar todas as expansões , o último dos quais é Quote Removal :

Quote removal shall always be performed last.

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

Segundo.

Como consequê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 cotação é removido, fazendo com que o primeiro $ não seja cotado.
Fim do primeiro loop.

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

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

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

Substitua eval por echo 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>

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

Uma correção.

Deve-se dizer que: há um erro no código acima, você pode vê-lo?
Fácil: há algumas citações faltando.

Como? 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 porque o valor dentro de $b foi dividido pelo shell.

Se isso não convencer você, 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 que funcione corretamente (adicione "$a" interno e externo \" quotes).
Tente isso (é perfeitamente seguro):

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

Sobre o manual:

There is no man page for it..

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

Está incluído dentro de man bash . Como é qualquer built-in.
Procure por "SHELL BUILTIN COMMANDS" e depois por "eval".

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

Por que o eval é chamado de mal?

Porque está vinculando texto ao código dinamicamente.

Em outras palavras: ele converte a lista de seus argumentos (e / ou expansões de tais argumentos) em uma linha executada. Se por algum 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 quem definiu o valor de um ou vários argumentos:

C'mon, sit here and type any command line, I will execute it with my powers.

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

A regra de segurança para eval deve ser:
   Apenas execute eval em variáveis para as quais você atribuiu seu valor.

Leia mais detalhes aqui .

    
por 19.12.2015 / 05:28
1

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
    
por 26.02.2018 / 10:06

Tags