web-dev-qa-db-fra.com

Comparaison en virgule flottante avec variable en bash

Je veux comparer une variable à virgule flottante à un entier. Je sais que ce n'est pas la meilleure chose à faire avec bash, mais tout mon script est déjà écrit en bash. $ nombre peut être n'importe quel entier. Si elle est inférieure ou égale à 50, je veux la sortie 1, pour toutes les autres, je veux une sortie avec l'autre variable k. Voici ce que j'ai jusqu'à présent:

number=43
test=$(echo "scale=2; $number/50" | bc -l)
echo "$test"
for k in {1..5}
do
    if ["$test" -le 1]
    then echo "output"

    Elif ["$test" -gt $k]
    then echo "output$k"
    fi
done

Si j'essaie avec test = 0,43, la première boucle ne fonctionne même pas. Je pense que cela a à voir avec un entier et une comparaison à virgule flottante, mais ne peut pas le faire fonctionner.

Quelque chose me manque?

PS: ce [0.43: command not found est ce que le terminal sort.

23
user1983400

Bash ne peut pas gérer les flotteurs. Dirigez-vous vers bc à la place:

if [ $(echo " $test > $k" | bc) -eq 1 ]

L'erreur que vous voyez est due au fait que la commande test (c'est-à-dire la [) a besoin d'espaces avant et après

Il vaut encore mieux utiliser (( ... )) puisque vous comparez des nombres comme celui-ci:

if (( $(bc <<< "$test > $k") ))

La partie de la boucle devrait ressembler à ceci:

if (( $(bc <<< "$test <= 1") ))
then
    echo "output"
Elif (( $(bc <<< "$test > $k") ))
then
    echo "output$k"
fi

Les expressions relationnelles sont évaluées à 0 si la relation est fausse et à 1 si la relation est vraie [ source ]. Notez cependant que c'est un comportement de GNU bc, et ce n'est pas POSIX compatible.

44
user000001

Une sorte de vieille question, mais elle porte une réponse supplémentaire je pense.

Bien que la tuyauterie vers une calculatrice de précision supérieure (bc ou dc) fonctionne, cela se fait au prix d'une fourchette et d'un processus supplémentaire, car ces calculatrices ne sont pas intégrées à bash. Une chose qui IS intégré, cependant, est printf. Donc, si vous pouvez gérer vos nombres dans un nombre particulier de décimales, vous pouvez "faux" virgule flottante comparaisons, avec une fonction comme celle-ci:

#!/usr/bin/env bash

function [[[ () {
  local LANG=C lhs rhs
  printf -v lhs '%07.3f' "$1"; lhs=${lhs/./}
  printf -v rhs '%07.3f' "$3"; rhs=${rhs/./}
  case "$2" in
    -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;;
    -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;;
    -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;;
    -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;;
    -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;;
  esac
}

number=${1:-43}
test=$(dc -e "2k $number 50 / p")
echo "$test"

for k in {1..5}; do
    if [[[ "$test" -le 1 ]]]; then
      echo "output"
    Elif [[[ "$test" -gt "$k" ]]]; then
      echo "output $k"
    fi
done

Quelques points à considérer ici.

  • J'ai nommé la fonction [[[ pour être mignon. Vous pouvez le nommer comme bon vous semble. ntest ou mynumericcomparison ou même [[[.
  • printf est une fonction interne dans bash, donc malgré le fait qu'elle se trouve sur votre chemin, elle ne coûte pas un fork.
  • En l'état, la fonction prend en charge les nombres jusqu'à 999,999. Si vous avez besoin de nombres plus élevés (ou plus précis), ajustez les formats printf.
  • Le 10# au début de chaque variable à l'intérieur de l'instruction case est de forcer la comparaison à se produire à la base 10, car un nombre complété par zéro pourrait autrement être interprété comme octal.

Voir aussi: http://mywiki.wooledge.org/BashFAQ/022

10
ghoti