diferença entre 'para i em {1 .. $ N}' e 'para i em $ (seq 1 1 $ N)' [duplicado]

5

Eu tenho o seguinte em um script:

#!/usr/bin/env bash 
...
for i in {1..$N}
   <loop content>

O acima não funciona e é executado apenas uma vez, em que i tem o valor {1..5} (se 5 foi fornecido como argumento).

Se eu substituir o acima com:

for i in $(seq 1 1 $N)

.. então as coisas funcionam como esperado. Por que isso?

    
por Marcus Junius Brutus 29.11.2013 / 21:20

2 respostas

8

{1..8} é uma forma de expansão de chaves introduzida por zsh e suportada por versões recentes de bash e ksh93 . Em bash , isso é feito muito cedo. Em particular, é feito antes expansão da variável.

Embora {1..8} seja válido, {1..$N} não é porque $N não é um número decimal, são os dois caracteres $ e N nenhum dos quais são dígitos decimais.

Mesmo que isso funcionasse, isso não seria muito eficiente. O shell teria que alocar na memória uma lista feita dos números 1 para $N . Se $N fosse 10 9 , isso significaria alocar gigabytes de memória.

$(seq "$N") é ainda menos eficiente. Primeiro, não é portátil. seq é um comando GNU não padrão. Em seguida, fazer $(seq "$N") significa iniciar um novo processo, executar seq nele, o shell ler sua saída por meio de um pipe e armazená-lo na memória, depois dividir essa lista (de acordo com $IFS ) e tentar executar a geração de nome de arquivo isso.

O espaço para a lista resultante dessa divisão e globbing também deve ser alocado.

Uma maneira mais eficiente de fazer isso é usar o estilo de loop ksh for (( )) (também suportado por bash e zsh por décadas).

for ((i = 1; i <= N; i++)); do
   <loop content>
done

Ou simplesmente escreva-o da maneira padrão e portátil, assim você não precisa usar bash , e pode usar seu (provavelmente mais rápido / mais magro) sistema sh :

i=1
while [ "$i" -le "$N" ]; do
  <loop content>
  i=$(($i + 1))
done

Ou talvez você possa fazer isso da maneira mais fácil, evitando loops completamente. Mas precisamos saber o que você quer fazer para ajudar lá.

    
por 29.11.2013 / 21:41
2

@ 1_CR está certo . Esta é uma duplicata. As expansões de chaves acontecem primeiro, antes que a variável $N seja expandida. Daí porque o primeiro exemplo não funciona. Você está tentando ativar a expansão 1 para um literal $N .

Exemplo

O que você está tentando fazer:

$ N=5
$ echo {1..$N}
{1..5}

Quando valores reais são usados:

$ echo {1..5}
1 2 3 4 5

Você também não está comparando maçãs com maçãs aqui. O que lhe dá a impressão de que {1..$N} é equiv. para $(seq 1 1 $N) ?

Além de produzir uma seqüência de números, as chaves ( {..} ) e o sub-shell ($ (..) ') são duas notações completamente diferentes.

    
por 29.11.2013 / 21:41

Tags