Порівняння чисел у Bash


546

Я починаю дізнаватися про написання сценаріїв для терміналу 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 більше, ніж 10, але 1 більше, ніж 09.

Як я можу перетворити числа у тип, щоб зробити справжнє порівняння?


1
Основне читання: BashFAQ
Édouard Lopez

6
До речі, в баші напівкрапка є роздільником висловлювань, а не термінатором операторів, який є новим рядком. Тож якщо у вас є лише одне твердження на рядку, то ;в кінці рядка зайві. Не заподіюючи шкоди, лише марно натискаючи на клавіші (якщо тільки вам не подобається вводити напівколонки).
cdarke

6
Змусити числа з провідними нулями в десяткові числа: 10#$numberтак number=09; echo "$((10#$number))"буде виводитися, 9а при цьому echo $((number))з'явиться помилка "значення, занадто велике для бази".
Призупинено до подальшого повідомлення.

4
Усі відповіді говорять про те, що правильно, але не те, що неправильно: те, що >робить оператор у [команді, - це порівняти порядок, за яким слід впорядкувати два рядки, а не порядок, який вони б сортували за числами. Ви можете знайти більше інформації в man test.
користувач3035772

Відповіді:


879

В bash, ви повинні зробити свою перевірку в арифметичному контексті :

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

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

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

Ви можете отримати повний список операторів порівняння з help testабо man test.


7
За словами @jordanm, "$a" -gt "$b"це правильна відповідь. Ось хороший список тестового оператора: Тестові конструкції .
Джеффі Томас

Це, безумовно, працює, але я все одно отримую "((: 09: значення занадто велике для базової (маркер помилки" 09 ")", якщо я порівнюю 1 і 09, але не 01 і 09, що не дивно, але це в основному вирішено моя проблема, тому дякую!
advert2013

8
@ advert2013 не слід префіксувати числа нулями. нульові префіксні числа - восьмеричні в баші
Алекс-Даніель Якименко-А.

8
Остерігайтеся, що testце така програма, як є [. Так help testдає інформацію про це. Щоб дізнатися, якими вбудованими ( [[і (() ви користуєтесь, слід help bashперейти до цієї частини.
RedX

1
Арифметичні вирази чудові, але операнди трактуються як вирази .
x-yuri

179

Простий і простий

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

Я просто скасував вашу іншу зміну - подвійні цитати навколо "$a"і "$b"не є суворо необхідними, але вони є хорошою практикою. Кучеряві брекети тут не роблять нічого корисного.
Том Фенек

1
чудова шпаргалка, яку ви пов’язали, не знайшла її раніше - тепер баш вже не здається такою магічною і непередбачуваною - дякую!
Ілля

котирування "обов'язкові чи це просто [ $a -eq $b ]також добре?
derHugo

Котирування @derHugo необов’язкові. Жил має краще пояснення, коли ними користуватися unix.stackexchange.com/a/68748/50394
Daniel Andrei Mincă

Основна відповідь + ця відповідь = досконала
Габріель Степлес

38

Є також одна приємна річ, про яку люди можуть не знати:

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

Цей код буде надрукувати найменше число з aіb


5
Це не правда. Він також надрукував bби, якщо a == b.
konsolebox

88
@konsolebox це тільки я, або найменше число з 5 і 5 - це 5?
Алекс-Даніель Якименко-А.

4
Ваше твердження неоднозначне. Навіть застосування такої команди не обійдеться:echo "The smaller number is $(( a < b ? a : b ))."
konsolebox

4
Що він говорить, a < bце все-таки правда, якщо a == b. Я не знаю всіх капризних умов Баша, але майже напевно існують ситуації, коли це мало б значення.
бікеле

4
@bikemule Ні, він цього не каже. Якщо a == b, тоді a < bоцінюється як хибне, через що воно буде надруковано b.
mapeters

21

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

[[ N -gt M ]]

Якщо я не займаюся подібними предметами

(( (N + 1) > M ))

Але у кожного просто свої переваги. Сумно в тому, що деякі люди нав'язують свої неофіційні стандарти.

Оновлення:

Ви також можете це зробити:

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

Що дозволяє вам додати щось інше, що ви могли б зробити, [[ ]]крім арифметичних речей.


3
Це, мабуть, означає, що це [[ ]]змушує арифметичний контекст на зразок (( )), коли до нього Nзвертаються так, як ніби $N, але я не думаю, що це правильно. Або, якщо це не було наміром, використання Nі Mє заплутаним.
Бенджамін В.

@ BenjaminW.Це вимагатиме підтвердження від Chet, але -eq, -ne, -lt, -le, -gt і -ge - це форми "арифметичних тестів" (задокументовано), які можуть означати, що операнди підлягають арифметичним виразам, як ну ..
konsolebox

Дякуємо, що повернулися до цього, оскільки ви абсолютно праві, і в посібнику це чітко сказано: "У разі використання [[команди Arg1і Arg2оцінюються як арифметичні вирази [...]".
Бенджамін В.

У мене є, NUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; doі там написаноbash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
Аарон Франке

@AaronFranke Арифметика Bash не підтримує десяткові символи.
konsolebox

6

Цей код також може порівнювати поплавці. Він використовує awk (це не чистий баш), однак це не повинно бути проблемою, оскільки 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

1
Я працюю з великою кількістю і 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).
jaume

3

Якщо у вас є поплавці, ви можете написати функцію, а потім використовувати її, наприклад

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

3

Наклейки в дужках (наприклад, [[ $a -gt $b ]]або (( $a > $b ))) недостатньо, якщо ви також хочете використовувати float числа; вона повідомила б про синтаксичну помилку. Якщо ви хочете порівняти числа з плаваючою чи числом плаваючого числа на цілі, ви можете використовувати (( $(bc <<< "...") )).

Наприклад,

a=2.00
b=1

if (( $(bc <<<"$a > $b") )); then 
    echo "a is greater than b"
else
    echo "a is not greater than b"
fi

Ви можете включити більше ніж одне порівняння в оператор if. Наприклад,

a=2.
b=1
c=1.0000

if (( $(bc <<<"$b == $c && $b < $a") )); then 
    echo "b is equal to c but less than a"
else
    echo "b is either not equal to c and/or not less than a"
fi

Це корисно, якщо ви хочете перевірити, чи числова змінна (ціле чи ні) знаходиться в числовому діапазоні.


Це не працює для мене. Наскільки я можу сказати, команда bc не повертає значення виходу, а замість цього друкує "1", якщо порівняння є істинним (і "0" в іншому випадку). Я повинен написати це замість цього:if [ "$(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b; fi
Terje

@TerjeMikal Ви хочете сказати, що для вашої команди if [ $(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b"; fi? (Я думаю, що ваша команда була неправильно написана.) Якщо так, то це теж працює. Команда Bash Calculator (bc) - це основна команда калькулятора. Ще кілька прикладів використання тут і тут . Я не знаю, чому моя приклад команда не працювала для вас.
LC-datascientist

2

Я вирішив це за допомогою невеликої функції для перетворення рядків версії в прості цілі значення, які можна порівняти:

function versionToInt() {
  local IFS=.
  parts=($1)
  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

за допомогою 'sor -V' ви можете сортувати номери версій, а потім вирішувати, що робити потім. Ви можете написати функцію порівняння так: функція version_lt () {test "$ (printf '% s \ n'" $ @ "| сортувати -V | head -n 1)" == "$ 1"; } і використовувати його так: якщо версія_lt $ v1 $ v2; потім ...
koem
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.