Sintaxe do loop de shell for script

190

Eu consegui o seguinte para trabalhar:

for i in {2..10}
do
    echo "output: $i"
done

Ela produz um grupo de linhas de output: 2, output: 3, assim por diante.

No entanto, tentando executar o seguinte:

max=10
for i in {2..$max}
do
    echo "$i"
done

produz o seguinte:

output: {2..10}

Como faço para o compilador perceber que ele deve tratar $ max como a outra extremidade da matriz, e não parte de uma string?

eykanal
fonte
2
qual sistema e shell você está usando? Que tipo de sistema pateta tem sh ou bash, mas não tem seq, um coreutil?
Whatsisname 18/09/09
11
O FreeBSD não.
Nietzche-jou
echo "$ideveria ser echo "$i"- não vai resolver o problema, no entanto.
Dave Jarvis
Estilo pequeno nit: normalmente vejo as palavras-chave doe thenna mesma linha que fore if, respectivamente. Por exemplo,for i in {2..10}; do
um nerd pago

Respostas:

234

A expansão de chaves , {x..y} é executada antes de outras expansões, portanto, você não pode usá- las para sequências de comprimento variável.

Em vez disso, use o seq 2 $maxmétodo como o usuário mob declarou .

Então, para o seu exemplo, seria:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
whatsisname
fonte
Não há um bom motivo para usar um comando externo, como seqcontar e incrementar números no loop for, portanto, é recomendável evitar o uso seq. Este comando é deixado para compatibilidade com o bash antigo. Os comandos internos são rápidos o suficiente. for (( EXP1; EXP2; EXP3 )) ...
miller
13
@iller, a for (( ... ))sintaxe não é POSIX e isso significa, por exemplo, que não funcionará imediatamente em coisas como o Alpine Linux. seqfaz.
Wernight 30/09/16
@Wernight Não é apenas o Alpine Linux; #!/bin/shé Dash no Debian / Ubuntu.
Franklin Yu
72

Experimente a versão de expressão aritmética de for:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

Está disponível na maioria das versões do bash e também deve ser compatível com o Bourne shell (sh).

PAUSE do sistema
fonte
1
@Flow: Hum, eu apenas tentei em alguns sistemas (baseados em Linux e BSD) com #! / Bin / sh e funcionou bem. Chamado em bash e especificamente em / bin / sh, ainda funcionou. Talvez a versão do sh seja importante? Você está em algum Unix antigo?
system PAUSE
8
Muito poucos sistemas têm um dedicado sh, tornando-o um link para outro outro shell. Idealmente, tal shell invocado como shseria única apoiar essas características no padrão POSIX, mas por padrão deixar alguns de seus recursos extras completamente. O loop for do estilo C não é um recurso POSIX, mas pode estar no shmodo pelo shell real.
chepner
27

Pise o loop manualmente:

i = 0
max = 10
enquanto [$ i -lt $ max]
Faz
    eco "output: $ i"
    true $ ((i ++))
feito

Se você não precisa ser totalmente POSIX, pode usar o loop for aritmético :

max = 10
for ((i = 0; i <max; i ++)); faça eco "output: $ i"; feito

Ou use jot (1) em sistemas BSD:

para i em $ (anotação 0 10); faça eco "output: $ i"; feito
Nietzche-jou
fonte
3
true $(( i++ ))não funciona em todos os casos; portanto, a maioria dos portáteis seria true $((i=i+1)).
Fluxo
ponto e vírgula não deve aparecer "para ((i = 0; i <máx; i ++))";
Logan
9

Há mais de uma maneira de fazer isso.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
efémero
fonte
7
Um risco de segurança, pois evalavaliará tudo o que você definir max. Considere max="2}; echo ha; #"e substitua echo hapor algo mais destrutivo.
chepner
6
(S) ele definiu max para 10. Sem risco.
Conrad Meyer
9

Se o seqcomando disponível no seu sistema:

for i in `seq 2 $max`
do
  echo "output: $i"
done

Caso contrário, use o homem pobre seqcom perl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

Assista essas aspas.

multidão
fonte
Acabei de verificar isso; não parece que é.
Eykanal 18/09/09
5

Esta é uma maneira:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

O modo Bash acima funcionará para kshe zshtambém quando bash -cfor substituído por ksh -cou zsh -crespectivamente.

Nota: for i in {2..${max}}; do echo $i; donetrabalha em zshe ksh.

Jahid
fonte
4

Podemos iterar o loop como na programação C.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done
rashedcs
fonte
2

Aqui ele trabalhou no Mac OS X.

Inclui o exemplo de uma data BSD, como incrementar e diminuir a data também:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done
minhas23
fonte
2

Bem, como não tinha o seqcomando instalado no meu sistema (Mac OS X versão 10.6.1 (Snow Leopard)), acabei usando um whileloop:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

* Encolhe os ombros * O que quer que funcione.

eykanal
fonte
2
seq é relativamente novo. Eu só descobri isso há alguns meses atrás. Mas você pode usar um loop 'for' !! A desvantagem de um 'tempo' é que você deve se lembrar de incrementar o contador em algum lugar dentro do loop, ou então fazer um loop para baixo.
system PAUSE
1

Todos eles fazem {1..8}e devem ser POSIX. Eles também não serão interrompidos se você colocar uma condicional continueno loop. O caminho canônico:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

Outra maneira:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

e outro:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

fonte
1

Usar:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

Você precisa da chamada explícita 'eval' para reavaliar o {} após a substituição da variável.

Chris Dodd
fonte