Сравнение номеров в Bash

Я начинаю узнавать о написании сценариев для терминала bash, но я не могу понять, как заставить сравнения работать правильно. Сценарий, который я использую:

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;

проблема в том, что он сравнивает число с первой цифры, т. е. 9 больше 10000, но 1 больше 09

Как я могу преобразовать числа в тип, чтобы сделать истинное сравнение?

7 ответов


в Баш, вы должны сделать вашу регистрацию в арифметические контексте:

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

для оболочек POSIX, которые не поддерживают (()), вы можете использовать -lt и -gt.

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

вы можете получить полный список операторов сравнения с help test.


легко и просто

#!/bin/bash

a=2462620
b=2462620

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

Вы можете ознакомиться эта шпаргалка если вы хотите больше сравнений чисел в замечательном мире сценариев Bash.

Короче говоря, целые числа можно сравнить только с:

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

есть также одна хорошая вещь, о которой некоторые люди могут не знать:

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

этот код будет печатать наименьшее количество из a и b


в Bash я предпочитаю делать это, поскольку он обращается больше как условная операция, в отличие от использования (( )) что больше арифметики.

[[ N -gt M ]]

если я не делаю сложные вещи, как

(( (N + 1) > M ))

но у каждого есть свои предпочтения. Печально, что некоторые люди навязывают свои неофициальные стандарты.

обновление:

вы также можете сделать это:

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

что позволяет добавить что-то еще что вы могли бы сделать с [[ ]] кроме арифметики питания.


этот код также может сравнивать поплавки. Он использует awk (это не чистый bash), однако это не должно быть проблемой, так как awk-это стандартная команда POSIX, которая, скорее всего, поставляется по умолчанию с вашей операционной системой.

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

чтобы сделать его короче для использования, использовать эту функцию:

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=
   op=
   num2=
   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

Я решил это, используя небольшую функцию для преобразования строк версии в простые целочисленные значения, которые можно сравнить:

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

это делает два важных допущения:

  1. вход-это " обычная строка SemVer"
  2. каждая часть находится между 0-999
versionToInt 12.34.56  # --> 12034056
versionToInt 1.2.3     # -->  1002003

пример проверки npm команда соотвествует минимальные ...

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

Если у вас есть поплавки, вы можете написать функцию, а затем использовать ее, например

#!/bin/bash

function float_gt() {
    perl -e "{if(>){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