38

我有这个脚本应该确保用户当前的 PHP 版本在一定范围内,虽然它应该工作,但某处存在一个错误,使它认为版本超出范围,有人可以看看并告诉我我能做些什么来解决它?

function version { echo "$@" | gawk -F. '{ printf("%d.%d.%d\n", $1,$2,$3); }'; }

phpver=`php -v |grep -Eow '^PHP [^ ]+' |gawk '{ print $2 }'`

if [ $(version $phpver) > $(version 5.2.13) ] || [ $(version $phpver) < $(version 5.2.13) ]; then
  echo "PHP Version $phpver must be between 5.2.13 - 5.3.15"
  exit
fi
4

10 回答 10

103

以下是比较版本的方法。

使用sort -V

function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }

示例用法:

first_version=5.100.2
second_version=5.1.2
if version_gt $first_version $second_version; then
     echo "$first_version is greater than $second_version !"
fi

亲:

  • 比较花哨的版本字符串的可靠方法:
    • 支持任何长度的子部分(即:1.3alpha.2.dev2 > 1.1 ?)
    • 支持字母排序(即:1.alpha < 1.beta2)
    • 支持大尺寸版本(即:1.10003939209329320932 > 1.2039209378273789273?)
  • 可以很容易地修改以支持 n 个参数。(留作练习;))
    • 通常使用 3 个参数非常有用:(即:1.2 < my_version < 2.7 )

缺点:

  • 对不同的程序使用很多不同的调用。所以效率不高。
  • 使用相当新的版本,sort它可能在您的系统上不可用。(检查man sort

没有sort -V

## each separate version number must be less than 3 digit wide !
function version { echo "$@" | gawk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; }

示例用法:

first_version=5.100.2
second_version=5.1.2
if [ "$(version "$first_version")" -gt "$(version "$second_version")" ]; then
     echo "$first_version is greater than $second_version !"
fi

亲:

  • 更快的解决方案,因为它只调用 1 个子进程
  • 更兼容的解决方案。

缺点:

  • 非常具体,版本字符串必须:
    • 仅具有 1、2 或 3 个部件的版本。(不包括“2.1.3.1”)
    • 每个部分只能是数字(不包括“3.1a”)
    • 每个部分不能大于 999(不包括 '1.20140417')

关于您的脚本的评论:

我看不出它是如何工作的:

  • 如评论中所述,>并且<是非常特殊的外壳字符,您应该将它们替换为-gtand-lt
  • 即使您替换了字符,也无法将版本号作为整数或浮点数进行比较。例如,在我的系统上,php 版本是5.5.9-1ubuntu4.

但是您的函数version()已经编写得很巧妙,并且可以通过规避按字母顺序排序数字不会按数字排序数字的经典问题来帮助您(按字母顺序 1 < 11 < 2,这在数字上是错误的)。但请注意:bash 不支持任意大的数字(如果您的目标是与 32 位系统兼容,请尽量保持在 32 位以下,因此这将是 9 位长数字)。因此,我修改了您的代码(在第二种方法中不使用sort -V)以强制版本字符串的每个部分仅使用 3 位数字。

编辑:应用@phk 改进,因为它明显更聪明,并在第一个版本中使用sort. 谢谢。

于 2014-06-05T18:12:20.903 回答
17

有可能进行 deb-distribution:

dpkg --compare-versions <version> <relation> <version>

例如:

dpkg --compare-versions "0.0.4" "gt" "0.0.3"
if [ $? -eq "0" ]; then echo "YES"; else echo "NO"; fi
于 2017-09-19T12:03:46.263 回答
4

它正在进行词汇比较。使用其中之一:

if [ $(version $phpver) -gt $(version 5.2.13) ] || [ $(version $phpver) -lt $(version 5.2.13) ]; then
if [[ $(version $phpver) > $(version 5.2.13) ]] || [[ $(version $phpver) < $(version 5.2.13) ]]; then
if (( $(version $phpver) > $(version 5.2.13) )) || (( $(version $phpver) < $(version 5.2.13) )); then

或者在 awk 或其他工具中完成所有操作。它正在尖叫着进行一些优化。看起来你也没有产生数字,所以你的设计很奇怪。通常版本子字符串乘以 1000,然后全部相加得到一个可比较的标量。

于 2013-06-07T17:19:25.380 回答
2

这是另一个解决方案:

  • 不运行任何外部命令tr
  • 版本字符串中的部分数量没有限制
  • 可以比较不同部分的版本字符串

请注意,它是使用数组变量的 Bash 代码。

compare_versions()
{
    local v1=( $(echo "$1" | tr '.' ' ') )
    local v2=( $(echo "$2" | tr '.' ' ') )
    local len="$(max "${#v1[*]}" "${#v2[*]}")"
    for ((i=0; i<len; i++))
    do
        [ "${v1[i]:-0}" -gt "${v2[i]:-0}" ] && return 1
        [ "${v1[i]:-0}" -lt "${v2[i]:-0}" ] && return 2
    done
    return 0
}

函数返回:

  • 0 如果版本相等(顺便说一句:1.2 == 1.2.0)
  • 1 如果第一个版本更大/更新
  • 2 如果第二个版本更大/更新

但是#1 - 它需要一个额外的功能(但功能min无论如何都非常有用):

min()
{
    local m="$1"
    for n in "$@"
    do
        [ "$n" -lt "$m" ] && m="$n"
    done
    echo "$m"
}

但是#2 - 它不能将版本字符串与字母数字部分进行比较(尽管实际上并不难添加)。

于 2016-11-21T11:39:05.083 回答
1

以下解决方案应该更准确地满足您的确切需求。它可用于测试CURRENT版本字符串是否介于MIN和之间MAX。我假设MINandMAX是可接受的版本号(即MIN <= CURRENT <= MAX,而不是MIN < CURRENT < MAX)。

# Usage: version MIN CURRENT MAX
version(){
    local h t v

    [[ $2 = "$1" || $2 = "$3" ]] && return 0

    v=$(printf '%s\n' "$@" | sort -V)
    h=$(head -n1 <<<"$v")
    t=$(tail -n1 <<<"$v")

    [[ $2 != "$h" && $2 != "$t" ]]
}

例如...

if ! version 5.2.13 "$phpver" 5.3.15; then
    echo "PHP Version $phpver must be between 5.2.13 - 5.3.15"
    exit
fi
于 2015-06-20T03:38:19.813 回答
1

如果您使用的是旧版本的 Bash 3 sort(看看您的 macOS ...),那么我创建了以下帮助脚本,您可以从中获取(也可以作为命令运行):

https://github.com/unicorn-fail/version_compare

于 2016-08-22T19:21:12.830 回答
1

测试 PHP CLI 版本的一个更安全的选择是使用 PHP 自己的version_compare函数。

#!/bin/bash

MIN_VERSION="7.0.0"
MAX_VERSION="7.1.4"
PHP_VERSION=`php -r 'echo PHP_VERSION;'`

function version_compare() {
    COMPARE_OP=$1;
    TEST_VERSION=$2;
    RESULT=$(php -r 'echo version_compare(PHP_VERSION, "'${TEST_VERSION}'", "'${COMPARE_OP}'") ? "TRUE" : "";')

    test -n "${RESULT}";
}

if ( version_compare "<" "${MIN_VERSION}" || version_compare ">" "${MAX_VERSION}" ); then
    echo "PHP Version ${PHP_VERSION} must be between ${MIN_VERSION} - ${MAX_VERSION}";
    exit 1;
fi

echo "PHP Version ${PHP_VERSION} is good!";
于 2017-05-04T15:54:21.020 回答
0

我为类似的问题写了这个不优雅的函数。 如果 arg1 小于或等于 arg2,vers_limit将返回 0,否则返回非 0:

vers_limit()
{
VERNEW=$1
VERLMT=$2

CHKNEW=$VERNEW
CHKLMT=$VERLMT

PASSED=

while :
do
        PARTNEW=${CHKNEW%%.*}
        PARTLMT=${CHKLMT%%.*}
        if [[ $PARTNEW -lt $PARTLMT ]]
        then
                PASSED=GOOD
                break
        elif [[ $PARTNEW -gt $PARTLMT ]]
        then
                PASSED=
                break
        else
                NXTNEW=${CHKNEW#*.}
                if [[ $NXTNEW == $CHKNEW ]]
                then
                        if [[ $NXTNEW == $CHKLMT ]]
                        then
                                PASSED=GOOD
                                break
                        else
                                NXTNEW=0
                        fi
                fi
                NXTLMT=${CHKLMT#*.}
                if [[ $NXTLMT == $CHKLMT ]]
                then
                        NXTLMT=0
                fi
        fi
        CHKNEW=$NXTNEW
        CHKLMT=$NXTLMT
done
test "$PASSED"
}

这不像这里的其他一些解决方案那样紧凑,也没有提供 3 路状态(即,更少、相等、更大),尽管我相信人们可以对参数进行排序、解释通过/失败状态和/或调用两次以完成任何所需的结果。也就是说,vers_limit确实具有某些优点:

  1. 无需调用外部实用程序,例如 sort、awk、gawk、tr 等。

  2. 处理任意大小的数字版本(直到 shell 对整数计算的限制)

  3. 处理任意数量的“点”部分。

于 2018-10-04T21:13:44.327 回答
0
v_min="5.2.15"
v_max="5.3.15"
v_php="$(php -v | head -1 | awk '{print $2}')"

[ ! "$v_php" = "$(echo -e "$v_php\n$v_min\n$v_max" | sort -V | head -2 | tail -1)" ] && {
  echo "PHP Version $v_php must be between $v_min - $v_max"
  exit
}

这会将v_min,v_maxv_php按版本顺序放置并测试是否v_php不在中间。

于 2019-05-06T22:40:48.860 回答
0

纯重击

我找到了访问此页面的方法,因为我遇到了同样的问题。其他的答案都不让我满意,所以我写了这个函数。
只要 2 个版本中的句点数相同,这将正确比较版本。
它执行 ac 风格的 for 循环,将 $i 从 0 逐渐设置为版本字符串中的数字 #。
对于每个 #:
如果 new - old 是 neg 我们知道第一个版本更新。
如果 new - old 是 pos,我们知道第一个版本较旧。
如果 new - old 为 0 那么它是相同的,我们需要继续检查。
在 $1 == $2 版本完全相同的情况下,我们运行 false 来设置函数的退出状态。

newver=1.10.1
installedver=1.9.25
#installedver=1.11.25
#installedver=1.10.1

checkupdate(){
# $1 = new version
# $2 = installed version
IFS='.' read -r -a nver <<< "$1"
IFS='.' read -r -a iver <<< "$2"
for ((i = 0 ; i < "${#nver[@]}" ; i++)) ;do
 case "$((${nver[i]}-${iver[i]}))" in
  -*) return 1 ;;
   0) ;;
   *) return 0 ;;
 esac
 false
done
}
checkupdate "$newver" "$installedver" && echo yes || echo no

SH的另一种方法

在我尝试在 Android 上实现上述功能后,我意识到我不会总是使用 bash,因此上述功能对我不起作用。这是我使用 awk 编写的版本来解决需要 bash 的问题:

checkupdate(){
# $1 = new version
# $2 = installed version

i=1
#we start at 1 and go until number of . so we can use our counter as awk position
places=$(awk -F. '{print NF+1}' <<< "$1")
while (( "$i" < "$places" )) ;do
 npos=$(awk -v pos=$i -F. '{print $pos}' <<< "$1")
 ipos=$(awk -v pos=$i -F. '{print $pos}' <<< "$2")
 case "$(( $npos - $ipos ))" in
  -*) return 1 ;;
   0) ;;
   *) return 0 ;;
 esac
 i=$((i+1))
 false
done
}
于 2020-05-16T12:03:56.000 回答