web-dev-qa-db-fra.com

Comparer les nombres dans Bash

Je commence à apprendre à écrire des scripts pour le terminal bash, mais je ne parviens pas à faire en sorte que les comparaisons fonctionnent correctement. Le script que j'utilise est:

echo "enter two numbers";
read a b;

echo "a=$a";
echo "b=$b";

if [ $a \> $b ];
then 
    echo "a is greater than b";
else
    echo "b is greater than a";
fi;

Le problème est qu’il compare le nombre à partir du premier chiffre, c’est-à-dire que 9 est supérieur à 10, mais que 1 est supérieur à 09.

Comment puis-je convertir les nombres en un type pour faire une vraie comparaison?

430
advert2013

En bash, vous devriez faire votre vérification dans contexte arithmétique :

if (( a > b )); then
    ...
fi

Pour les shells POSIX qui ne prennent pas en charge (()), vous pouvez utiliser -lt et -gt.

if [ "$a" -gt "$b" ]; then
    ...
fi

Vous pouvez obtenir une liste complète des opérateurs de comparaison avec help test.

734
jordanm

Simple et clair

#!/bin/bash

a=2462620
b=2462620

if [ "$a" -eq "$b" ];then
  echo "They're equal";
fi

Vous pouvez vérifier cette feuille de triche si vous voulez plus de comparaisons de nombres dans le monde merveilleux des scripts Bash.

En bref, les entiers ne peuvent être comparés qu'avec:

-eq # equal
-ne # not equal
-lt # less than
-le # less than or equal
-gt # greater than
-ge # greater than or equal
136

Il y a aussi une bonne chose que certaines personnes pourraient ne pas savoir:

echo $(( a < b ? a : b ))

Ce code imprimera le plus petit nombre parmi a et b

Dans Bash, je préfère le faire car cela s’adresse davantage à une opération conditionnelle, contrairement à l’utilisation de (( )) qui est plus arithmétique.

[[ N -gt M ]]

Sauf si je fais des choses complexes comme

(( (N + 1) > M ))

Mais tout le monde a ses propres préférences. Ce qui est triste, c'est que certaines personnes imposent leurs normes non officielles.

Mise à jour:

Vous pouvez également faire ceci:

[[ 'N + 1' -gt M ]]

Ce qui vous permet d’ajouter quelque chose que vous pourriez faire avec [[ ]] en plus des choses arithmétiques.

17
konsolebox

Ce code peut également comparer les flottants. Il utilise awk (ce n'est pas du pur bash), mais cela ne devrait pas poser de problème, car awk est une commande POSIX standard livrée très probablement par défaut avec votre système d'exploitation.

$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
1
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?

Pour raccourcir l'utilisation, utilisez cette fonction:

compare_nums()
{
   # Function to compare two numbers (float or integers) by using awk.
   # The function will not print anything, but it will return 0 (if the comparison is true) or 1
   # (if the comparison is false) exit codes, so it can be used directly in Shell one liners.
   #############
   ### Usage ###
   ### Note that you have to enclose the comparison operator in quotes.
   #############
   # compare_nums 1 ">" 2 # returns false
   # compare_nums 1.23 "<=" 2 # returns true
   # compare_nums -1.238 "<=" -2 # returns false
   #############################################
   num1=$1
   op=$2
   num2=$3
   E_BADARGS=65

   # Make sure that the provided numbers are actually numbers.
   if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi
   if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi

   # If you want to print the exit code as well (instead of only returning it), uncomment
   # the awk line below and comment the uncommented one which is two lines below.
   #awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
   awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
   return_code=$?
   return $return_code
}

$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false
true
$ compare_nums -1.2345 ">=" 23 && echo true || echo false
false
5
Vangelis Tasoulas

J'ai résolu ce problème en utilisant une petite fonction pour convertir les chaînes de version en valeurs entières pouvant être comparées:

function versionToInt() {
  local IFS=.
  parts=($1)
  let val=1000000*parts[0]+1000*parts[1]+parts[2]
  echo $val
}

Cela fait deux hypothèses importantes:

  1. L'entrée est un " chaîne de SemVer normale "
  2. Chaque partie est comprise entre 0 et 999

Par exemple

versionToInt 12.34.56  # --> 12034056
versionToInt 1.2.3     # -->  1002003

Exemple permettant de vérifier si la commande npm est conforme à la configuration minimale requise ...

NPM_ACTUAL=$(versionToInt $(npm --version))  # Capture npm version
NPM_REQUIRED=$(versionToInt 4.3.0)           # Desired version
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then
  echo "Please update to npm@latest"
  exit 1
fi
2
broofa

Si vous avez des flottants, vous pouvez écrire une fonction puis l’utiliser, par exemple.

#!/bin/bash

function float_gt() {
    Perl -e "{if($1>$2){print 1} else {print 0}}"
}

x=3.14
y=5.20
if [ $(float_gt $x $y) == 1 ] ; then
    echo "do stuff with x"
else
    echo "do stuff with y"
fi
0
Sue