在Bash中比较数字


545

我开始学习有关为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
爱德华·洛佩兹

6
顺便说一句,在bash中,分号是语句分隔符,而不是语句终止符,它是换行符。因此,如果一行上只有一个语句,那么行;末尾就多余了。没有造成任何伤害,只是浪费了按键(除非您喜欢输入分号)。
cdarke

6
要强制带前导零的数字为小数:10#$number这样number=09; echo "$((10#$number))"会输出9,同时echo $((number))会产生一个“值太大,基地”的错误。
暂停,直到另行通知。

4
答案全都告诉您什么是对的,但不是什么错:>运算符在[命令中所做的是比较两个字符串应该排序的顺序,而不是它们作为数字排序的顺序。您可以在中找到更多信息man test
user3035772

Answers:


878

在bash中,您应该在算术上下文中进行检查:

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

对于不支持的POSIX shell,(())可以使用-lt-gt

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

您可以使用help test或来获得比较运算符的完整列表man test


7
正如@jordanm所说,这"$a" -gt "$b"是正确的答案。这是测试操作员的一个不错的清单:Test Constructs
杰弗里·托马斯

如果我比较1和09而不是01和09,那绝对是可行的,但是我仍然得到“((:09:value for base for too too(错误标记是“ 09”)“),但这基本上已经解决了我的问题,所以谢谢!
advert2013 2013年

8
@ advert2013,您不应在数字前加零。零前缀数字是八进制在bash
阿莱克斯-丹尼尔Jakimenko-A。

8
当心这test是一个程序[。因此help test提供了有关的信息。要找出内置函数([[((),您应该使用help bash并导航至该部分。
RedX 2015年

1
算术表达式很棒,但是操作数被视为表达式
x-yuri

179

干净利落

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

我只是取消了您的其他更改-周围没有双引号"$a""$b"严格来说也没有必要,但这是很好的做法。花括号在这里没有任何用处。
汤姆·费内奇

1
您链接过的很棒的备忘单,以前从未找到过-现在bash似乎不再那么魔术和不可预测了-谢谢!
伊利亚(Ilja)'18

引号是"强制性的还是[ $a -eq $b ]还可以?
derHugo

@derHugo引号是可选的。Gilles对于何时使用它们有更好的解释unix.stackexchange.com/a/68748/50394
Daniel AndreiMincă18年

主要答案 +该答案=完美
Gabriel Staples

38

有些人可能不知道的一件好事:

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

这段代码将打印数量最少出ab


5
这不是真的。它也将打印b,如果a == b
konsolebox 2013年

88
@konsolebox是我一个人,还是5和5中最小的数字是5?
Aleks-Daniel Jakimenko-A。

4
您的陈述含糊。即使应用这样的命令也不会:echo "The smaller number is $(( a < b ? a : b ))."
konsolebox 2013年

4
他的意思是,a < b如果仍然如此a == b。我不知道Bash有条件的所有变数,但是几乎可以肯定,在某些情况下这会有所作为。
bikemule

4
@bikemule不,他不是在说那个。如果a == b,则结果a < b为false,这就是为什么要打印的原因b
mapeters'7

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

感谢您回到正确的位置,并且您完全正确,并且手册中明确指出:“当与[[命令一起使用时,Arg1Arg2作为算术表达式求值”。
本杰明·

我有NUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; do,它说bash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
Aaron Franke

@AaronFranke Bash算法不支持小数。
konsolebox

6

此代码还可以比较浮点数。它使用的是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

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

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

使用“ sort -V”,您可以对版本号进行排序,然后决定要做什么。您可以像这样编写一个比较函数:function version_lt(){test“ $(printf'%s \ n'” $ @“ | sort -V | head -n 1)” ==“ $ 1”; },然后像这样使用它:if version_lt $ v1 $ v2; 然后...
koem

2

如果您也想使用浮点数,则仅用括号(例如[[ $a -gt $b ]](( $a > $b )))是不够的;它会报告语法错误。如果要将浮点数或浮点数与整数进行比较,可以使用(( $(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 Mikal,

@TerjeMikal对于您的命令,是什么意思if [ $(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b"; fi?(我认为您的命令写错了。)如果是这样,那也行得通。Bash计算器(bc)命令是基本的计算器命令。在此处此处可以找到更多用法示例。我不知道为什么我的示例命令对您不起作用。
LC数据科学家,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.