在 if 语句和运算符中使用带小数点的数字

在 if 语句和运算符中使用带小数点的数字

我正在尝试编写一个脚本来检测 Linux 操作系统和版本,并通过使用条件和运算符根据结果执行不同的命令。

检测操作系统

if [ -f /etc/os-release ]; then 
    . /etc/os-release 
    OS="$NAME" 
    OS_version="$VERSION_ID" 
elif [ -f /etc/debian_version ]; then 
    # Older Debian/Ubuntu/etc. 
    OS="Debian" 
    OS_version="$(cat /etc/debian_version)" 
fi

如果我回显这些变量,它们会返回

$ echo $OS 
Debian GNU/Linux

$ echo $OS_version 
10

在下面的示例中我想匹配德班和操作系统版本10。但是,那$操作系统变量不仅仅包含单词德班所以我需要某种通配符,这样我就可以进行部分匹配。

我发现通过命令替换来使其工作的唯一方法是回显$操作系统变量到 stdout 并将其输送到 grep 命令中,然后执行通配符搜索。这是我最后想出的 if 语句:

if [ "$(echo "$OS" | grep 'Debian*')" -a "$OS_version" -ge 10 ]; then 
    echo "OS is Debian" 
    sleep 4 
else  
    echo "OS is other" 
    sleep 4 
fi

由于我将在其他基于 Unix 的操作系统上使用此脚本,因此我希望使此脚本尽可能广泛兼容并遵守 POSIX 标准。

在 Ubuntu 21.04 上测试我的脚本时,我发现当涉及小数位时,Linux 很难计算出哪个数字大于另一个数字。下面我创建了两个 shell 脚本,它们导出一个带有两位小数的两个整数,并使用 if 语句检查它是否大于和/或等于另一个数字。

20.10.sh

#!/bin/sh
export OS_version="20.10"

if [ "$OS_version" -ge 21.04 ]; then
    echo "Your OS is new enough"
    sleep 2
else
    echo "Your OS is too old!"
    sleep 2
fi

20.10.sh输出

~$ ./20.10.sh 
YOUR OS VERSION IS >> 20.10 
./20.10.sh: 6: [: Illegal number: 20.10 
Your OS is too old!

23.45.sh

#!/bin/sh
export OS_version="23.45" 
if [ "$OS_version" -ge 21.04 ]; then 
    echo "Your OS is new enough" 
    sleep 2 
else 
    echo "Your OS is too old!" 
    sleep 2 
fi

23.45.sh输出

~$ ./23.45.sh 
YOUR OS VERSION IS >> 23.45 
./23.45.sh: 6: [: Illegal number: 23.45 
Your OS is too old!

23.45.sh脚本应该回显“您的操作系统足够新”,但没有回显并显示非法号码的错误。

有没有办法检测带小数位的不同数字的大小?

答案1

标准[命令仅支持十进制整数作为其数字比较运算符。某些[实现(例如[内置的)ksh93确实支持浮点数以及扩展,但这不可移植,并且无论如何浮点数比较与版本比较不同。

例如10.2,浮点数大于10.10,但当它们被解释为版本时则相反。

Ubuntu 版本是日期。 20.04(2020年4月)是在20.10(2010年10月)之前。对于这些,您可以使用词法比较(可以使用 进行awk),前提是您不要追溯到 9.10 (Karmic Koala) 或更早版本,并且不包括次要数字(如 18.04.4 中)(或者如果它确实永远不会达到 10)。

1.2pre5-beta1.2小于1.2while1.2a通常被认为大于1.2。如果不知道版本命名约定,通常很难比较版本。

在 GNU 系统上,您可能希望遵循 GNUsort--version-sort算法。

version_greater_than() {
  local before_sort="$1"$'\n'"$2"
  [ "$before_sort" != "$(printf %s "$before_sort" | sort --stable --version-sort)" ]
}

(在 ksh93/zsh/bash 语法中)或其sh等效语法:

version_greater_than() {
  [ "$1
$2" != "$(printf '%s\n' "$1" "$2" | sort --stable --version-sort)" ]
}

然后,你可以这样做:

if version_greater_than 10.10 10.4; then
  echo 10.10 greater than 10.4
fi

POSIXly,您可以采取一种awk方法,将所有数字序列用零填充到任意长度,并在 C 语言环境中进行词法比较:

compare_versions() {
  LC_ALL=C awk -- '
    function pad(v,  ret) {
      while (match(v, /[0-9]+/)) {
        ret = ret substr(v, 1, RSTART - 1) \
              sprintf("%09d", substr(v, RSTART, RLENGTH))
        v = substr(v, RSTART + RLENGTH)
      }
      return ret v
    }
    BEGIN {exit !(pad(ARGV[1]) '"$2"' pad(ARGV[2]))}' "$1" "$3"
}

用作:

if compare_versions 1.10 '>' 1.2; then
  echo '1.10 > 1.2'
fi

答案2

您不能使用 或 等运算符-lt进行-ge定点或浮点比较;它们是严格的整数数学运算符。 bash(例如)不知道如何做非整数数学。您必须使用 eg<或 来将版本号作为字符串进行比较>=,同时还要注意 eg1.1是“大于” 1.10。您可以通过构建sort -v逻辑来解决这个问题。

答案3

只要版本号是浮点数(而不是像 20.04.3 这样的东西),您就可以将比较通过管道传输到bc.

例如,更改此:

if [ "$OS_version" -ge 21.04 ]; then

对此:

if [ $(echo "$OS_version >= 21.04" | bc) ]; then

相关内容