如何在Shell脚本中比较程序的版本?


14

假设我想比较gcc版本以查看系统是否安装了最低版本。

要检查gcc版本,我执行了以下命令

gcc --version | head -n1 | cut -d" " -f4

输出是

4.8.5

因此,我写了一个简单的if语句来对照其他值检查该版本

if [ "$(gcc --version | head -n1 | cut -d" " -f4)" -lt 5.0.0 ]; then
    echo "Less than 5.0.0"
else
    echo "Greater than 5.0.0"
fi

但这会引发错误:

[: integer expression expected: 4.8.5

我理解我使用字符串进行比较并且-lt需要整数的错误。那么,还有其他方法可以比较版本吗?


@ 123什么都没发生
Abhimanyu Saharan

1
还有一个堆栈溢出问题,其中包含一堆用于比较版本字符串的不同建议。
n.st

1
比使用管道更简单:gcc -dumpversion
Victor Lamoine

Answers:


22

我不知道它是否漂亮,但是它适用于我所知道的每种版本格式。

#!/bin/bash
currentver="$(gcc -dumpversion)"
requiredver="5.0.0"
 if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then 
        echo "Greater than or equal to 5.0.0"
 else
        echo "Less than 5.0.0"
 fi

注意:用户“通配符”的更好版本:https : //unix.stackexchange.com/users/135943/wildcard,删除了附加条件)


3
起初我以为这太糟糕了,然后我意识到shell脚本之美正是在这样的滥用工具中。+1
Monica Cellio的抵制SE

2
如果打印语句中有“%”符号,则会中断此操作。更好的替代printf "$requiredver\n$currentver"使用printf '%s\n' "$requiredver" "$currentver"
phk

1
-V是GNU扩展,sort(1)因此该解决方案不可移植。
stefanct

1
sort -n对于数字版本,其工作方式几乎相同。
Rockallite

1
@LucianoAndressMartini,看看您如何看待我的编辑。
通配符'18

2

在这里,我给出了一个比较Unix内核版本的解决方案。它应该对其他人如gcc起作用。我只关心前2个版本号,但是您可以添加另一层逻辑。这是一本班轮,为了理解我多行编写。

check_linux_version() {
    version_good=$(uname -r | awk 'BEGIN{ FS="."}; 
    { if ($1 < 4) { print "N"; } 
      else if ($1 == 4) { 
          if ($2 < 4) { print "N"; } 
          else { print "Y"; } 
      } 
      else { print "Y"; }
    }')

    #if [ "$current" \< "$expected" ]; then
    if [ "$version_good" = "N" ]; then
        current=$(uname -r)
        echo current linux version too low
        echo current Linux: $current
        echo required 4.4 minimum
        return 1
    fi
}

您可以修改它并将其用于gcc版本检查。


+1是否与其他类Unix兼容?(我的解决方案不是)
Luciano Andress Martini

1

我们曾经在GNU makefile中做过很多版本检查。我们通过makefile工具进行了安装。我们必须检测旧的Binutils和错误的编译器,并即时解决它们。

我们使用的模式是:

#!/usr/bin/env bash

CC=$(command -v gcc)
GREP=$(command -v grep)

# Fixup CC and GREP as needed. It may be needed on AIX, BSDs, and Solaris
if [[ -f "/usr/gnu/bin/grep" ]]; then
    GREP="/usr/gnu/bin/grep"
elif [[ -f "/usr/linux/bin/grep" ]]; then
    GREP="/usr/linux/bin/grep"
elif [[ -f "/usr/xpg4/bin/grep" ]]; then
    GREP="/usr/xpg4/bin/grep"
fi

# Check compiler for GCC 4.8 or later
GCC48_OR_LATER=$("$CXX" -v 2>&1 | "$GREP" -i -c -E "gcc version (4\.[8-9]|[5-9]\.)")
if [[ "$GCC48_OR_LATER" -ne 0 ]];
then
   ...
fi

# Check assembler for GAS 2.19 or later
GAS219_OR_LATER=$("$CXX" -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | "$GREP" -c -E "GNU assembler version (2\.19|2\.[2-9]|[3-9])")
if [[ "$GAS219_OR_LATER" -ne 0 ]];
then
   ...
fi

真好!这有效并且超级容易扩展,并且非常便携!
布拉德·帕克斯

1

短版:

version_greater_equal()
{
    printf '%s\n%s\n' "$2" "$1" | sort -V -C
}

version_greater_equal "${gcc_version}" 8.2 || die "need 8.2 or above"

(1)这是已经给出的答案的一个很小的变化。您可以通过添加尚未发布的解释来增加价值。(2)  printf '%s\n'足够好;printf将根据需要重复格式字符串。
G-Man说“恢复莫妮卡”

我通常更喜欢编辑现有答案,但是删除其中一半比较棘手:其他人可能会看到有价值的内容,而我却没有。相同的详细说明。少即是多。
MarcH

我知道printf重复格式字符串,但是我的语法(缺乏!)是恕我直言的;因此,仅当参数=较大或可变时,才需要使用此方法。
MarcH

1
function version_compare () {
  function sub_ver () {
    local len=${#1}
    temp=${1%%"."*} && indexOf=`echo ${1%%"."*} | echo ${#temp}`
    echo -e "${1:0:indexOf}"
  }
  function cut_dot () {
    local offset=${#1}
    local length=${#2}
    echo -e "${2:((++offset)):length}"
  }
  if [ -z "$1" ] || [ -z "$2" ]; then
    echo "=" && exit 0
  fi
  local v1=`echo -e "${1}" | tr -d '[[:space:]]'`
  local v2=`echo -e "${2}" | tr -d '[[:space:]]'`
  local v1_sub=`sub_ver $v1`
  local v2_sub=`sub_ver $v2`
  if (( v1_sub > v2_sub )); then
    echo ">"
  elif (( v1_sub < v2_sub )); then
    echo "<"
  else
    version_compare `cut_dot $v1_sub $v1` `cut_dot $v2_sub $v2`
  fi
}

### Usage:

version_compare "1.2.3" "1.2.4"
# Output: <

幸得@Shellman

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.