Eu gostaria de armazenar um comando para usar em um período posterior em uma variável (não a saída do comando, mas o próprio comando)
Tenho um script simples como segue:
command="ls";
echo "Command: $command"; #Output is: Command: ls
b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)
No entanto, quando tento algo um pouco mais complicado, falha. Por exemplo, se eu fizer
command="ls | grep -c '^'";
O resultado é:
Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory
Alguma ideia de como eu poderia armazenar esse comando (com tubos / comandos múltiplos) em uma variável para uso posterior?
Respostas:
Use eval:
fonte
backticks
. y = $ (eval $ x) mywiki.wooledge.org/BashFAQ/082eval
é uma prática aceitável apenas se você confiar no conteúdo de suas variáveis. Se você estiver executando, digamos,x="ls $name | wc"
(ou mesmox="ls '$name' | wc"
), então este código é um caminho rápido para vulnerabilidades de injeção ou escalonamento de privilégio se essa variável puder ser definida por alguém com menos privilégios. (Iterando sobre todos os subdiretórios/tmp
, por exemplo? É melhor você confiar que cada usuário do sistema não fará nenhum chamado$'/tmp/evil-$(rm -rf $HOME)\'$(rm -rf $HOME)\'/'
).eval
é um grande ímã de bug que nunca deve ser recomendado sem um aviso sobre o risco de comportamento de análise inesperado (mesmo sem strings maliciosas, como no exemplo de @CharlesDuffy). Por exemplo, tentex='echo $(( 6 * 7 ))'
e entãoeval $x
. Você pode esperar que imprima "42", mas provavelmente não irá. Você pode explicar por que não funciona? Você pode explicar por que eu disse "provavelmente"? Se as respostas a essas perguntas não forem óbvias para você, nunca toqueeval
.set -x
antes para registrar a execução dos comandos, o que tornará mais fácil ver o que está acontecendo.Você não usar
eval
! Há um grande risco de introdução de execução arbitrária de código.BashFAQ-50 - Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham.
Coloque-o em uma matriz e expanda todas as palavras com aspas duplas
"${arr[@]}"
para não permitir queIFS
as palavras sejam separadas devido à Divisão de Palavras .e veja o conteúdo da matriz interna. O
declare -p
permite que você veja o conteúdo do array dentro de cada parâmetro de comando em índices separados. Se um desses argumentos contiver espaços, aspas internas ao adicionar ao array evitarão que ele seja dividido devido à divisão de palavras.e execute os comandos como
(ou) usar uma
bash
função para executar o comando,e chamar a função apenas
POSIX
sh
não tem matrizes, então o mais próximo que você pode chegar é construir uma lista de elementos nos parâmetros posicionais. Esta é umash
maneira POSIX de executar um programa de e-mailObserve que essa abordagem só pode lidar com comandos simples, sem redirecionamentos. Ele não pode lidar com redirecionamentos, pipelines, loops for / while, instruções if, etc.
Outro caso de uso comum é ao executar
curl
com vários campos de cabeçalho e carga útil. Você sempre pode definir args como abaixo e invocarcurl
no conteúdo da matriz expandidaOutro exemplo,
agora que as variáveis estão definidas, use uma matriz para armazenar seus argumentos de comando
e agora faça uma expansão adequada entre aspas
fonte
Command=('echo aaa | grep a')
e"${Command[@]}"
, esperando que execute literalmente o comandoecho aaa | grep a
. Não é verdade. Gostaria de saber se existe uma maneira segura de substituireval
, mas parece que cada solução que tem a mesma forçaeval
pode ser perigosa. Não é?Command() { echo aaa | grep a; }
- após a qual você pode simplesmente executarCommand
, ouresult=$(Command)
, ou algo semelhante.Usando este método, o comando é avaliado imediatamente e seu valor de retorno é armazenado.
Mesmo com backtick
Usar eval no
$(...)
não fará com que seja avaliado mais tardeUsando eval, ele é avaliado quando
eval
é usadoNo exemplo acima, se você precisa executar um comando com argumentos, coloque-os na string que você está armazenando
Para scripts bash, isso raramente é relevante, exceto uma última observação. Tenha cuidado com
eval
. Avalie apenas as strings que você controla, nunca as strings vindas de um usuário não confiável ou criadas a partir de entrada de usuário não confiável.fonte
eval $stored_date
pode ser suficiente quandostored_date
apenas contémdate
, maseval "$stored_date"
é muito mais confiável. Executestr=$'printf \' * %s\\n\' *'; eval "$str"
com e sem as aspas no final"$str"
para um exemplo. :)Para o bash, armazene seu comando assim:
Execute seu comando assim:
fonte
Tentei vários métodos diferentes:
Resultado:
Como você pode ver, apenas o terceiro
"$@"
deu o resultado correto.fonte
Tenha cuidado ao registrar um pedido com:
X=$(Command)
Este ainda é executado Mesmo antes de ser chamado. Para verificar e confirmar isso, você pode fazer:
fonte
fonte
Não é necessário armazenar comandos em variáveis, mesmo que você precise usá-los posteriormente. apenas execute-o normalmente. Se você armazenar em uma variável, precisará de algum tipo de
eval
instrução ou invocará algum processo shell desnecessário para "executar sua variável".fonte
var='*.txt'; find . -name "$var"