Executa um comando armazenado em uma variável

8

Eu tenho um comando armazenado em uma variável. Vamos fingir que a variável $i tem o valor:

cat -nT index.php |grep 'someregex'

Quando tento executar a variável acima digitando $i , ele falha porque o shell tenta executar a variável inteira como um comando. Eu também tentei usar eval($i) e colocar $i nos backticks.

Como posso fazer o shell executar $i como se fosse um comando? E por que não está funcionando da mesma forma?

$i='echo hi'; $i

É porque eu tive que cortar aspas simples? (Porque você não pode aninhar-los.) Atualmente minha solução é

echo $i > /foo;  . /foo

Mas não quero criar um arquivo apenas para isso, apenas para apagá-lo depois.

O que quero dizer com "eu hackeei as aspas simples é o que fiz:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"
    
por Leathan 24.11.2013 / 09:23

3 respostas

14

Resposta curta: veja BashAQ # 50: Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham! .

Resposta longa: é por causa da ordem em que o bash analisa a linha de comando. Especificamente, ele procura por coisas como pipes e redirecionamentos antes de expandir os valores das variáveis, e não volta e procura por canos, etc. nos valores expandidos. Essencialmente, as variáveis são substituídas na metade do processo de análise, de modo que o valor só é analisado pela metade antes de ser executado.

Para resolver isso, você precisa responder a pergunta do @shhck: por que o comando é armazenado em uma variável em primeiro lugar? Qual é o problema real que você está tentando resolver? Dependendo do objetivo real, há várias soluções possíveis:

  • Não armazene em uma variável, apenas execute diretamente. Armazenar comandos para uso posterior é complicado, e se você realmente não precisa, simplesmente não faça isso.

  • Use uma função em vez de uma variável. É para isso que eles servem, afinal de contas:

    i() { cat -nT index.php |grep 'someregex'; }
    i
    

    A principal desvantagem disso é que você não pode criar a função dinamicamente - você não pode incluir ou excluir condicionalmente o código da função (embora a própria função possa ter elementos condicionais selecionados quando ela é executada).

  • Use eval . Isso deve ser considerado um último recurso, já que é fácil obter um comportamento inesperado. Ele essencialmente executa o comando por meio de outro passo de análise completo, de modo que todos os pipes etc. obtêm seu significado completo - mas também significa que partes do comando que você considerava apenas dados também serão analisadas e talvez executadas. Nomes de arquivos que contêm metacaracteres de shell (pipe, ponto e vírgula, citação / apóstrofo, etc) podem ter efeitos estranhos e às vezes perigosos. Se você usar eval , ao menos a aspas duplas, caso contrário, seu conteúdo será analisado uma vez e meia, com resultados ainda mais estranhos.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"
    

EDIT: Outra abordagem padrão de armazenar um comando em uma variável é usar uma matriz em vez de uma variável simples. Isso permite armazenar um comando com argumentos complexos (por exemplo, contendo espaços) sem problemas, mas não armazena coisas como pipes e redirecionamentos. Portanto, a abordagem da matriz não será útil nesse caso específico.

    
por 24.11.2013 / 21:20
4

Isso deve funcionar:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Tenha em atenção que eval introduz potenciais vulnerabilidades e situações de comportamento inesperadas, pelo que deve ser utilizado, se alguma vez, com cuidado extra.

    
por 24.11.2013 / 10:36
3

Ao declarar a variável, você não deve usar o cifrão como um prefixo.

Tente isto:

i='echo hi'; $i
    
por 24.11.2013 / 10:17