Сравнение чисел в Баш

233

Я начинаю узнавать о написании сценариев для терминала bash, но я не могу понять, как правильно сравнивать сравнения. Я уверен, что это очень важно для многих из вас. Я просто не могу найти ответ на свой вопрос в любом месте (или, по крайней мере, я не совсем уверен, что искать). script Я использую:

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. Как преобразовать числа в тип для сравнения?

  • 1
    Основное чтение: BashFAQ
  • 3
    Кстати, в bash точка с запятой - это разделитель операторов, а не терминатор операторов, который является новой строкой. Так что если у вас есть только одно утверждение в строке, тогда ; в конце строки лишние. Не причиняя никакого вреда, просто трата клавиш (если вы не любите вводить точки с запятой).
Показать ещё 2 комментария
Теги:

6 ответов

430
Лучший ответ

В bash вы должны сделать свой чек в арифметическом контексте:

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

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

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

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

  • 5
    Как сказал @jordanm "$a" -gt "$b" является правильным ответом. Вот хороший список операторов тестирования: Test Constructs .
  • 0
    Это определенно работает, но я все еще получаю "((: 09: значение слишком велико для базы (токен ошибки" 09 ")", если я сравниваю 1 и 09, но не 01 и 09, что странно, но это в основном решило моя проблема, так что спасибо!
Показать ещё 7 комментариев
41

Обычный и простой

#!/bin/bash

a=2462620
b=2462620

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

Вы можете проверить этот чит-лист, если вам нужно больше чисел в прекрасном мире Bash Scripting.

В скором времени целые числа можно сравнить только с:

-eq # equal
-ne # not equal
-lt # less than
-le # less than or equal
-gt # greater than
-ge # greater than or equal
  • 0
    Я просто отменил ваше другое изменение - двойные кавычки вокруг "$a" и "$b" не являются строго необходимыми, но это хорошая практика. Фигурные скобки здесь не помогают.
  • 1
    отличный чит-лист, который вы связали, не нашли его раньше - теперь bash больше не кажется таким волшебным и непредсказуемым - спасибо!
Показать ещё 2 комментария
28

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

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

Этот код напечатает наименьшее число из a и b

  • 3
    Это не правда. Также будет напечатано b если a == b .
  • 75
    @konsolebox это только у меня, или наименьшее число из 5 и 5 это 5?
Показать ещё 3 комментария
11

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

[[ N -gt M ]]

Если я не делаю сложные вещи, например

(( (N + 1) > M ))

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

Update:

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

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

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

  • 2
    Кажется, это подразумевает, что [[ ]] вызывает арифметический контекст типа (( )) , где N обрабатывается так, как если бы это было $N , но я не думаю, что это правильно. Или, если это не было намерением, использование N и M сбивает с толку.
4

Этот код также может сравнивать поплавки. Он использует 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=$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
  • 0
    Я работаю с большими числами, и bash не может их правильно сравнить (попробуйте if (( 18446744073692774399 < 8589934592 )); then echo 'integer overflow'; fi ). awk работает как талисман ( if awk "BEGIN {return_code=(18446744073692774399 > 8589934592) ? 0 : 1; exit} END {exit return_code}"; then echo 'no integer overflow'; fi ).
0

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

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

Это делает два важных предположения:

Например

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
  • 0
    с помощью 'sort -V' вы можете отсортировать номера версий и затем решить, что делать дальше. Вы можете написать функцию сравнения следующим образом: function version_lt () {test "$ (printf '% s \ n'" $ @ "| sort -V | head -n 1)" == "$ 1"; } и используйте его так: if version_lt $ v1 $ v2; затем ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню