Evite loops em shells.
Se você quiser fazer aritmética, use awk
ou bc
:
awk '
BEGIN{
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
}'
Ou
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Observe que awk
(ao contrário de bc
) funciona com seus processadores double
representação do número de ponto flutuante (provável IEEE 754 tipo). Como resultado, como esses números são aproximações binárias desses números decimais, você pode ter algumas surpresas:
$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2
Se você adicionar um OFMT="%.17g"
, poderá ver o motivo da falta de 0.3
:
$ gawk 'BEGIN{OFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i}'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc
faz precisão arbitrária, então não tem esse tipo de problema.
Observe que, por padrão (a menos que você modifique o formato de saída com OFMT
ou use printf
com especificações de formato explícitas), awk
usa %.6g
para exibir números de ponto flutuante, então alterne para 1e6 e superior para números de ponto flutuante acima de 1.000.000 e truncar a parte fracionária para números altos (100.000.02 seria exibido como 100.000).
Se você realmente precisa usar um loop de shell, porque, por exemplo, você quer executar comandos específicos para cada iteração desse loop, ou usar um shell com suporte aritmético de ponto flutuante como zsh
, yash
ou ksh93
ou gere a lista de valores com um comando como acima (ou seq
se disponível) e faça um loop sobre sua saída.
Como:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Ou:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
a menos que você exceda os limites dos números de ponto flutuante do seu processador, seq
manipula os erros incorridos pelas aproximações de ponto flutuante com mais graça do que a awk
versão acima faria.
Se você não tem seq
(um comando GNU), você pode fazer um mais confiável como uma função como:
seq() { # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
}
Isso funcionaria melhor para coisas como seq 100000000001 0.000000001 100000000001.000000005
. Note, entretanto, que ter números com precisão arbitrariamente alta não ajudará muito se formos passá-los para comandos que não os suportam.