Comparando números en Bash

Estoy comenzando a aprender a escribir guiones para la terminal de bash, pero no puedo encontrar la forma de hacer que las comparaciones funcionen correctamente. El script que estoy usando es:

echo "enter two numbers"; read ab; echo "a=$a"; echo "b=$b"; if [ $a \> $b ]; then echo "a is greater than b"; else echo "b is greater than a"; fi; 

El problema que tengo es que compara el número del primer dígito, es decir, 9 es más grande que 10000, pero 1 es mayor que 09

¿Cómo puedo convertir los números en un tipo para hacer una verdadera comparación?

En bash, debes hacer tu verificación en el contexto aritmético :

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

Para las shells POSIX que no admiten (()) , puede usar -lt y -gt .

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

Puede obtener una lista completa de operadores de comparación con la help test .

Simple y simple

 #!/bin/bash a=2462620 b=2462620 if [ "$a" -eq "$b" ];then echo "They're equal"; fi 

Puede consultar esta hoja de trucos si quiere más comparaciones de números en el maravilloso mundo de Bash Scripting.

En breve, los enteros solo se pueden comparar con:

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

También hay una cosa agradable que algunas personas podrían desconocer:

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

Este código imprimirá el número más pequeño de b

En Bash prefiero hacer esto, ya que se dirige más como una operación condicional a diferencia de usar (( )) que es más de aritmética.

 [[ N -gt M ]] 

A menos que haga cosas complejas como

 (( (N + 1) > M )) 

Pero todos solo tienen sus propias preferencias. Lo triste es que algunas personas imponen sus estándares no oficiales.

Actualizar:

Usted también puede hacer esto:

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

Lo cual le permite agregar algo más que podría hacer con [[ ]] además de elementos aritméticos.

Este código también puede comparar flotadores. Está utilizando awk (no es puro bash), sin embargo, esto no debería ser un problema, ya que awk es un comando POSIX estándar que muy probablemente se envíe de manera predeterminada con su sistema operativo.

 $ 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 $? 

Para acortarlo, use esta función:

 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 

Lo resolví usando una pequeña función para convertir cadenas de versión en valores enteros simples que se pueden comparar:

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

Esto hace dos suposiciones importantes:

  1. La entrada es una ” secuencia SemVer normal ”
  2. Cada parte está entre 0-999

Por ejemplo

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

Ejemplo de prueba de si el comando npm cumple con el requisito mínimo …

 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 

Si tiene flotadores puede escribir una función y luego usar eso, por ejemplo

 #!/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