Como combinar expansão aritmética e expansão de chaves? [duplicado]

2

As pessoas podem combinar expansão aritmética e fortalecer a expansão?

$ for i in {$((1 + 1))..5}; do echo $i; done;
{2..5}
$ echo "bash laughs at me"
    
por uprego 27.08.2014 / 15:05

2 respostas

4

Veja man bash para explicação:

The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.

A expansão de contraventamento acontece antes da expansão aritmética, então você não pode combiná-los da maneira como tentou.

Use seq :

for i in $(seq $((a+4)) 12) ; do echo $i ; done
    
por 27.08.2014 / 15:07
2
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
    
por 27.08.2014 / 16:43