如何比较 Bash 中的数字?

Posted

技术标签:

【中文标题】如何比较 Bash 中的数字?【英文标题】:How can I compare numbers in Bash? 【发布时间】:2013-09-11 04:34:03 【问题描述】:

我无法进行数字比较:

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。

如何将数字转换为类型以进行真正的比较?

【问题讨论】:

基础阅读:BashFAQ 顺便说一句,在 bash 中,分号是语句分隔符,而不是语句终止符,它是换行符。因此,如果您在一行中只有一个语句,那么行尾的 ; 是多余的。不会造成任何伤害,只是浪费击键(除非您喜欢键入分号)。 强制带前导零的数字为小数:10#$number 所以number=09; echo "$((10#$number))" 将输出9echo $((number)) 将产生“值对于基数来说太大”错误。 答案都告诉你什么是对的,而不是什么是错的:> 运算符在[ 命令中所做的是比较两个字符串应该排序的顺序,而不是它们的顺序将排序为数字。你可以在man test找到更多信息。 【参考方案1】:

在 Bash 中,您应该签入arithmetic context:

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

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

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

您可以使用help testman test 获取比较运算符的完整列表。

【讨论】:

正如@jordanm "$a" -gt "$b" 所说,是正确的答案。这是一个很好的测试运算符列表:Test Constructs. @advert2013 你不应该在数字前加上零。在 bash 中,零前缀数字是八进制 啊,好吧,那解决了那个谜 - 我真的没有任何用零前缀数字,但很高兴知道错误来自哪里 请注意test[ 一样是一个程序。所以help test 提供了相关信息。要找出内置插件([[((),您应该使用 help bash 并导航到该部分。 算术表达式很棒,但操作数被视为expressions。【参考方案2】:

在 Bash 中,我更喜欢这样做,因为它更多地将自身视为条件操作,而不像使用更多算术的 (( ))

[[ n -gt m ]]

除非我做复杂的事情,比如

(( (n + 1) > m ))

但每个人都有自己的喜好。可悲的是,有些人强加了他们的非官方标准。

您也可以这样做:

[[ 'n + 1' -gt m ]]

除了算术的东西之外,它还允许你添加一些你可以用[[ ]]做的东西。

【讨论】:

这似乎暗示[[ ]] 强制使用类似(( )) 的算术上下文,其中N 被视为$N,但我认为这是不正确的。或者,如果这不是本意,NM 的用法会令人困惑。 @BenjaminW。这需要 Chet 的确认,但 -eq、-ne、-lt、-le、-gt 和 -ge 是“算术测试”的形式(已记录),这可能意味着操作数也受算术表达式的影响.. 感谢您回到这里,因为您完全正确,the manual 明确指出:“与[[ 命令一起使用时,Arg1Arg2 被评估为算术表达式 [...]"。 我有NUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; do,上面写着bash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0") @AaronFranke Bash 算术不支持小数。【参考方案3】:

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

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

此代码将打印ab 中最小的数字

【讨论】:

这不是真的。如果a == b,它也会打印b @konsolebox 是我,还是 5 和 5 中最小的数字是 5? 您的说法不明确。即使应用这样的命令也不行:echo "The smaller number is $(( a &lt; b ? a : b ))." 他的意思是,如果a == ba &lt; b 仍然是正确的。我不知道 Bash 条件句的所有变幻莫测,但几乎可以肯定在某些情况下这会有所作为。 @bikemule 不,他不是这么说的。如果a == b,那么a &lt; b 的计算结果为假,这就是为什么它会打印b【参考方案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

【讨论】:

我正在处理大量数字,bash 无法正确比较它们(尝试if (( 18446744073692774399 &lt; 8589934592 )); then echo 'integer overflow'; fi)。 awk 就像一个魅力 (if awk "BEGIN return_code=(18446744073692774399 &gt; 8589934592) ? 0 : 1; exit END exit return_code"; then echo 'no integer overflow'; fi)。【参考方案5】:

像这样:

#!/bin/bash

a=2462620
b=2462620

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

整数可以与这些运算符进行比较:

-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal

见this cheatsheet。

【讨论】:

我刚刚取消了您的其他更改 - "$a""$b" 周围的双引号不是绝对必要的,但它们是很好的做法。花括号在这里没有任何用处。 您链接的很棒的备忘单,以前没有找到 - 现在 bash 似乎不再那么神奇和不可预测了 - 谢谢! 引号" 是强制性的还是只是[ $a -eq $b ] 也可以? @derHugo 引号是可选的。 Gilles 对何时使用它们有更好的解释unix.stackexchange.com/a/68748/50394 如果使用双括号,则不需要引号:if [[ $a -eq $b ]];then【参考方案6】:

我通过使用一个小函数将版本字符串转换为可以比较的纯整数值解决了这个问题:

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

这做了两个重要的假设:

    输入是“normal SemVer string” 每个部分都在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;然后... 如果版本字符串编号有前导零怎么办?它们会被解释为八进制数吗?【参考方案7】:

如果你有浮点数,你可以写一个函数然后使用它。例如,

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

【讨论】:

【参考方案8】:

如果您还想使用浮点数,括号内容(例如 [[ $a -gt $b ]](( $a &gt; $b )) )是不够的;它会报告语法错误。如果要将浮点数或浮点数与整数进行比较,可以使用(( $(bc &lt;&lt;&lt; "...") ))

例如,

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 &lt;&lt;&lt;"$a &gt; $b") == "1" ]; then echo "a is greater than b; fi @TerjeMikal 对于您的命令,您的意思是if [ $(bc &lt;&lt;&lt;"$a &gt; $b") == "1" ]; then echo "a is greater than b"; fi? (我认为你的命令写错了。)如果是这样,那也有效。 Bash Calculator (bc) 命令是一个基本的计算器命令。找到更多使用示例here 和here。我不知道为什么我的示例命令对你不起作用。【参考方案9】:

单线解决方案。

a=2
b=1
[[ $a -gt $b ]] && echo "true" || echo "false"

【讨论】:

解释一下。例如,想法/要点是什么?这是实现ternary operator的一种方式吗?来自the Help Center:“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。

以上是关于如何比较 Bash 中的数字?的主要内容,如果未能解决你的问题,请参考以下文章

BASH将Linux版本与数字进行比较? [重复]

如何在bash中的awk内动态比较变量?

如何将字符串与 Bash 中的多个正确值进行比较?

如何在Linux 系统上比较Bash 脚本中的字符串?

bash 中的字符串比较。 [[: 未找到

如何在 Bash 中比较两个点分隔版本格式的字符串?