Qual é a diferença entre "eval" e "source / dev / stdin"?

17

Entre as seguintes alternativas ...

  1. com eval.

    comd="ls"
    eval "$comd"
    
  2. com source /dev/stdin

    printf "ls" | source /dev/stdin
  3. com source /dev/stdine ( )ou{ }

    ( printf "ls" ) | source /dev/stdin
    { printf "ls"; } | source /dev/stdin
    

    (Quando corremos printfem { }, há algum benefício além de não usar subnível?)

    • Qual a diferença entre eles?

    • Qual é o preferido?

    • Qual é a maneira preferida de executar comandos? ()ou {}?

MS.Kim
fonte
11
Eu não recomendaria nenhuma das abordagens. O que você está realmente tentando fazer e acha que precisa executar código arbitrário enviado por um usuário?
chepner
2
Eu também pensei que eles estavam executando entradas arbitrárias de usuários (como estão), até eu ler a pergunta. Mas pode ser que você esteja prevendo que eles o farão.
Ctrl-alt-delor

Respostas:

17
  • Qual é a diferença entre as maneiras?

de bash manpage:

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.

source filename [arguments]
              Read and execute commands from filename  in  the  current  shell
              environment  and return the exit status of the last command exe
              cuted from filename.  If filename does not contain a slash, file
              names  in  PATH  are used to find the directory containing file
              name.  The file searched for in PATH  need  not  be  executable.
              When  bash  is  not  in  posix  mode,  the  current directory is
              searched if no file is found in PATH.  If the sourcepath  option
              to  the  shopt  builtin  command  is turned off, the PATH is not
              searched.  If any arguments are supplied, they become the  posi
              tional  parameters  when  filename  is  executed.  Otherwise the
              positional parameters are unchanged.  The return status  is  the
              status  of  the  last  command exited within the script (0 if no
              commands are executed), and false if filename is  not  found  or
              cannot be read.

Não há diferenças entre as duas maneiras.

Há apenas uma nota: evalconcatenou todos os seus argumentos, que são executados como um único comando. sourcelê o conteúdo de um arquivo e os executa. evalsó pode criar comandos a partir de seus argumentos, não stdin. Então você não pode fazer assim:

printf "ls" | eval
  • Qual é o mais preferido?

Seu exemplo fornece o mesmo resultado, mas o objetivo evale sourceé diferente. sourceé geralmente usado para fornecer uma biblioteca para outros scripts, enquanto evalé usado apenas para avaliar comandos. Você deve evitar o uso, evalse possível, porque não há garantia de que a cadeia avaliada esteja limpa; devemos fazer algumas verificações de sanidade, usando em seu subshelllugar.

  • Se executarmos alguns comandos em () ou {}, qual é o preferido?

Quando você executa comandos de sequência dentro de chaves { }, todos os comandos são executados no shell atual , em vez de um subshell (que é o caso se você executar entre parênteses (consulte a referência do bash )).

O uso subshell ( )usa mais recursos, mas seu ambiente atual não é afetado. O uso { }executa todos os comandos no shell atual, para que seu ambiente seja afetado. Dependendo do seu objetivo, você pode escolher um deles.

cuonglm
fonte
2
Eu acho que você não entendeu a pergunta. Claro, você não pode substituir evalpor source. Eu acho que a pergunta é: é eval "$cmd"equivalente a echo "$cmd" | source /dev/stdin. Minha opinião atual é: sim.
amigos estão dizendo sobre hauke laging
3

A principal diferença é que o 2º e o 3º formulários estão usando um canal, o que forçará o bash a executar o comando "source" em um subshell (a menos que lastpipe esteja definido, disponível apenas no bash 4.2+), o que o tornará praticamente equivalente a :

printf "ls" | bash

As conseqüências são que quaisquer variáveis ​​de ambiente definidas pelo seu código serão perdidas, portanto isso não funcionará conforme o esperado:

printf "abc=2" | source /dev/stdin

Para executar os comandos no shell atual, você pode usar a substituição de processo:

source <(printf "abc=2")

Você pode colocar mais comandos entre parênteses usando ponto e vírgula, como de costume.

Se você eliminar o pipe dessa maneira, acredito que não há diferença entre usar "eval" e "source". Você deve preferir o que é mais simples de usar no seu caso particular:

  • se você já possui comandos para executar na variável, use "eval"
  • se você os tiver em um arquivo ou obtê-los de comando externo, use "source"
Kamil Christ
fonte
0

Como complemento às respostas já dadas:

Um sourceequivalente a ...

comd="ls"
eval "$comd"

... é ...

source <(printf ls)

No caso de lsnão haver diferença significativa.

Porém, no caso de um comando que pretenda afetar seu ambiente atual (o que você normalmente pretende usar source), essa variante o faria (como sua primeira solução evaltambém faria), enquanto sua segunda abordagem afeta apenas o ambiente de um subshell que ganhou ' Não esteja disponível após a execução da sua linha de código.

yaccob
fonte