
Minha pesquisa esta manhã foi sobre como comparar dois números decimais no bash, e cheguei a esta resposta:Como comparar com o número de ponto flutuante em um script de shell. Este, no entanto, não incluiessa resposta aqui:
$ [[ ((3.56 < 2.90)) ]]; echo $?
1
$ [[ ((3.56 < 4.90)) ]]; echo $?
0
Considerando que a resposta foi rejeitada e parece algum tipo de bashismo incomum, esta avaliação aritmética é confiável quanto à precisão?
Responder1
bash
não entende números de ponto flutuante.
Citando bash
página de manual, seçãoAVALIAÇÃO ARITMÉTICA:
A avaliação é feita em números inteiros de largura fixa […].
Então ((3 < 4))
ou ((3 < 2))
são na verdade expressões aritméticas corretas. Você pode digitar o seguinte:
$ echo "$((3 < 4)) -- $((3 < 2))"
saída:
1 -- 0
Mas $ echo $((3.3 < 3.6))
retornará uma mensagem de erro de sintaxe. No seu exemplo, você está comparando strings. Daí alguns exemplos:
$ [[ ((3.56 < 04.90)) ]]; echo $?
saída:
1
Responder2
Dentro [[...]]
<
é para comparação de strings.
Então [[ 3.56 < 2.90 ]]
or [[ (3.56 < 2.90) ]]
or [[ ((3.56 < 2.90)) ]]
or [[ (((3.56 < 2.90))) ]]
... está apenas comparando a 3.56
string com a 2.90
string lexicamente (e lexicamente, 3
é maior que, 10
por exemplo).
Para comparação de números inteiros, é [[ 3 -lt 2 ]]
ou (( 3 < 2 ))
. Se você deseja uma comparação de ponto flutuante, você precisa ksh93
, zsh
ou yash
ou de um utilitário externo como awk
ou perl
; bash
não posso fazer isso.
Você poderia, por exemplo, definir uma função como:
compare() (IFS=" "
exec awk "BEGIN{if (!($*)) exit(1)}"
)
Que você pode usar, por exemplo, como:
if compare '1.5*10 < 1e3'; then
echo less
fi
Ou mesmo para isso importa:
if compare '"bar" < "foo"'...
para fazer comparações de strings.
Não passe dados não controlados fornecidos externamente para essa compare
função, pois isso constituiria uma vulnerabilidade de injeção de comando (os dados são interpretados como awk
código, awk
podem executar comandos com seus, system()
por exemplo).