eval 'for i in {'"$((1 + 1))"'..5}; do echo $i; done'
OUTPUT
2
3
4
5
Em geral, se você quer que o shell faça alguma coisa antes que ele realmente funcione, você deve dar uma segunda olhada no shell. Esta é a função eval
- avalia duas vezes um comando. O shell analisa sua avaliação de expansões de shell - o que geralmente não acontece.
Considere isso:
v='"quotes in here"'; printf %s\n $v
"quotes
in
here"
Você vê que as aspas são interpretadas pelo analisador do shell, que manipula as expansões antes de entrada, e assim as aspas dentro de $v
não têm significado lá - não há analisador para entendê-las . Mas se eu fizer:
v='"quotes in here"'; eval "printf '%s\n' $v"
quotes in here
A saída é de repente diferente. A diferença é feita pelo analisador - é o bit que decide quais bits de sua entrada são comandos e por quê. É a parte que funciona com ;${}()||''""&& while for if
de todo esse tipo de coisa. Isso não significa que as chaves {}
sejam equivalentes àquelas que você está perguntando - como a outra resposta já mostrou, elas aparentemente são tratadas separadamente como uma extensão bash
. Em qualquer caso, geralmente você precisa de alguma forma de segunda avaliação para lidar com isso.
É isso que torna eval
perigoso. Quando você "${expansion}"
cita uma expansão, você a marca para o analisador - você delimita . O shell sabe que, aconteça o que acontecer, isso é apenas um item - provavelmente um argumento - em um comando. E mesmo quando você não faz; o shell não aceitará um $expansion
que esteja fora de um limite de comando ;
simples - porque esse comando simples também já está delimitado . Mas - como acontece com eval
- se o shell voltar e olhar para essa expansão mais uma vez, ele pode encontrar um comando que pode ser executado - até mesmo vários - e é assim que eval
funciona.
Portanto, ao usar eval
, você precisa ter muito cuidado para não avaliar duas vezes inesperadamente um token de shell ou qualquer expansão que possa conter um - e você só avaliará na primeira rodada a parte que o shell não faz em breve o suficiente. A melhor prática é apenas avaliar individualmente qualquer parte do comando, mas em momentos diferentes, como é feito abaixo.
Aqui está a primeira string eval
novamente:
eval \ #inital command
'for i in {'\ #hard-quoted - not expanded or executed
"$((1 + 1))"\ #double-quoted - expanded and delimited
'..5}; do echo $i; done' #hard-quoted - not expanded or executed
Os comentários acima discutem as ações tomadas durante a passagem inicial de eval
- há mais uma para ir. Nesse caso, você deseja 1+1
. Então, primeiro expandimos isso e nada mais. O pior que podemos tirar disso é 2
. Este não é um token de analisador de shell e é seguro avaliar - e é até mesmo double-quoted. De fato, se houver algum uso real válido de eval
, é aritmético. Todo o resto é citado - não há nenhuma ação e as strings são simplesmente concatenadas e as aspas removidas, como acontece com qualquer outra string com aspas duras.
Mas quando voltar pela segunda vez - depois que as aspas forem removidas do primeiro passo - o shell descobrirá:
for i in {2..5}; do echo $i; done
E isso acontece.
Existem outras maneiras de fazer isso:
bash -c "for i in {$((1+1))..5}; do echo \$i; done"
^ que funciona.
E:
. /dev/fd/0 <<CMD
for i in {$((1+1))..5}; do echo \$i; done
CMD
^ isso funciona. Em todos os casos, a logística é a mesma - você avalia a variável antes de avaliar a expansão bash
brace.
Eu nunca tive muito uso para a expansão de brace, no entanto. Eu geralmente gosto:
until [ $((i=$i+1)) -gt 5 ]
do printf %d\n $i
done