Problemas aritméticos de Bash

14

Tentando bash arithemetic para um script, mas $e não atualiza até o final. A saída fala por si.

max=5
for e in $(seq 1 1 $max); do 
    percent=$(( $e/$max*100 ))
    echo "echo $e / $max : = $percent"
done

Tl; DR: Exibe 1..5 como uma porcentagem.

Saída:

echo 1 / 5 : = 0
echo 2 / 5 : = 0
echo 3 / 5 : = 0
echo 4 / 5 : = 0
echo 5 / 5 : = 100

Por que isso acontece?

    
por Cybex 16.09.2016 / 08:23

4 respostas

23

bash não pode lidar com aritmética não inteira. Ele lhe dará um resultado correto, desde que todas as expressões sejam números inteiros. Então você precisa evitar um valor não-inteiro em algum lugar no seu cálculo.

No seu caso 1 / 5 cria um valor não inteiro e o bash falha nas operações posteriores. Precedência da divisão e multiplicação são os mesmos e precedentes operadores precedentes são sempre executados da esquerda para a direita quando são colocados em a expressão.

Um trabalho em torno será fazer a multiplicação primeiro e depois a divisão de modo que o bash nunca tenha que manipular o valor não-inteiro. A expressão corrigida será,

$ max=5; for e in $(seq 1 1 $max); do percent=$(( $e*100/$max )); echo "echo $e / $max : = $percent"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100
    
por souravc 16.09.2016 / 08:52
16

Bash não se sai muito bem com este tipo de aritmética ... Aqui está o seu problema:

$ echo $((1/5))
0
$ echo $((2/5))
0
$ echo $((4/5))
0
$ echo $((4/5))
0

Se você precisa manipular valores não inteiros, você pode usar bc

$ max=5; for e in $(seq 1 1 "$max"); do percent=$(bc <<< "scale=1 ; $e/$max*100") ; echo "echo $e / $max : = ${percent%.*}"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

(graças a @Arronical por apontar como formatar a saída como números inteiros)

    
por Zanna 16.09.2016 / 08:45
11

Diferente do bash, o awk oferece aritmética de ponto flutuante completo. Por exemplo:

$ awk -v max=5 'BEGIN{for (e=1;e<=max;e++) print "echo " e " / " max " : = " 100*e/max}' 
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100
    
por John1024 16.09.2016 / 08:56
6

Tente

percent=$(( $e*100/$max ))

:)

Veja a seção AVALIAÇÃO ARITMÉTICA de:

man bash

Ele suporta apenas números inteiros.

    
por alfred 16.09.2016 / 08:31