使用 Bash 比较 PHP 版本号?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16989598/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Comparing PHP version numbers using Bash?
提问by ehime
I have this script that should make sure that the users current PHP version is between a certain range, though it SHOULD work, there is a bug somewhere that makes it think that the version is out of range, could someone take a look and tell me what I can do to fix it?
我有这个脚本应该确保用户当前的 PHP 版本在一定范围内,虽然它应该可以工作,但某处有一个错误使它认为版本超出范围,有人可以看看并告诉我我能做些什么来解决它?
function version { echo "$@" | gawk -F. '{ printf("%d.%d.%d\n", ,,); }'; }
phpver=`php -v |grep -Eow '^PHP [^ ]+' |gawk '{ print }'`
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
回答by vaab
Here's how to compare versions.
这是比较版本的方法。
using sort -V
:
使用sort -V
:
function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != ""; }
example usage:
用法示例:
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
pro:
亲:
- solid way to compare fancy version strings:
- support any length of sub-parts (ie: 1.3alpha.2.dev2 > 1.1 ?)
- support alpha-betical sort (ie: 1.alpha < 1.beta2)
- support big size version (ie: 1.10003939209329320932 > 1.2039209378273789273 ?)
- can easily be modified to support n arguments. (leaved as an exercise ;) )
- usually very usefull with 3 arguments: (ie: 1.2 < my_version < 2.7 )
- 比较花哨版本字符串的可靠方法:
- 支持任何长度的子部分(即:1.3alpha.2.dev2 > 1.1 ?)
- 支持字母排序(即:1.alpha < 1.beta2)
- 支持大尺寸版本(即:1.10003939209329320932 > 1.2039209378273789273 ?)
- 可以轻松修改以支持 n 个参数。(留作练习;))
- 通常非常有用,有 3 个参数:(即: 1.2 < my_version < 2.7 )
cons:
缺点:
- uses a lot of various calls to different programs. So it's not that efficient.
- uses a pretty recent version of
sort
and it might not be available on your system. (check withman sort
)
- 对不同的程序使用了很多不同的调用。所以效率不高。
- 使用最新版本的
sort
并且它可能在您的系统上不可用。(核对man sort
)
without sort -V
:
没有sort -V
:
## each separate version number must be less than 3 digit wide !
function version { echo "$@" | gawk -F. '{ printf("%03d%03d%03d\n", ,,); }'; }
example usage:
用法示例:
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
pro:
亲:
- quicker solution as it only calls 1 subprocess
- much more compatible solution.
- 更快的解决方案,因为它只调用 1 个子进程
- 更兼容的解决方案。
cons:
缺点:
- quite specific, version string must:
- have version with 1, 2 or 3 parts only. (excludes '2.1.3.1')
- each parts must be numerical only (excludes '3.1a')
- each part can't be greater than 999 (excludes '1.20140417')
- 非常具体,版本字符串必须:
- 有只有 1、2 或 3 个零件的版本。(不包括“2.1.3.1”)
- 每个部分只能是数字(不包括“3.1a”)
- 每个部分不能大于 999(不包括 '1.20140417')
Comments about your script:
关于你的脚本的评论:
I can't see how it could work:
我看不出它是如何工作的:
- as stated in a comment
>
and<
are very special shell character and you should replace them by-gt
and-lt
- even if you replaced the characters, you can't compare version numbers as if they where integers or float. For instance, on my system, php version is
5.5.9-1ubuntu4
.
- 如评论中所述,
>
并且<
是非常特殊的 shell 字符,您应该将它们替换为-gt
和-lt
- 即使您替换了字符,您也无法比较版本号,就好像它们是整数或浮点数一样。例如,在我的系统上,php 版本是
5.5.9-1ubuntu4
.
But your function version()
is quite cleverly written already and may help you by circumventing the classical issue that sorting alphabetically numbers won't sort numbers numerically ( alphabetically 1 < 11 < 2, which is wrong numerically). But be carefull: arbitrarily large numbers aren't supported by bash (try to keep under 32bits if you aim at compatibility with 32bits systems, so that would be 9 digit long numbers). So I've modified your code (in the second method NOT using sort -V
) to force only 3 digits for each part of the version string.
但是你的函数version()
已经写得很巧妙了,可以通过规避按字母排序数字不会按数字排序的经典问题来帮助你(按字母顺序 1 < 11 < 2,这在数字上是错误的)。但要小心:bash 不支持任意大的数字(如果您的目标是与 32 位系统兼容,请尽量保持在 32 位以下,这样将是 9 位长的数字)。因此,我修改了您的代码(在第二种方法中不使用sort -V
)以强制版本字符串的每个部分仅使用 3 位数字。
EDIT: applied @phk amelioration, as it is noticeably cleverer and remove a subprocess call in the first version using sort
. Thanks.
编辑:应用@phk 改进,因为它明显更聪明并且使用sort
. 谢谢。
回答by Andrey
There is possibility for deb-distributions:
有可能进行 deb 分布:
dpkg --compare-versions <version> <relation> <version>
For example:
例如:
dpkg --compare-versions "0.0.4" "gt" "0.0.3"
if [ $? -eq "0" ]; then echo "YES"; else echo "NO"; fi
回答by lynxlynxlynx
It is doing a lexical comparison. Use one of these:
它正在做一个词法比较。使用其中之一:
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
Or do it all in awk or some other tool. It is screaming for some optimisation. It also seems you're not producing numbers either, so you have a pretty odd design. Usually the version substrings are multiplied by 1000 and then all summed up to get a single comparable scalar.
或者在 awk 或其他一些工具中完成所有操作。它正在尖叫着进行一些优化。看起来你也没有产生数字,所以你的设计很奇怪。通常版本子串乘以 1000,然后全部相加得到一个可比较的标量。
回答by mato
Here's another solution that:
这是另一个解决方案:
- does not run any external command apart from
tr
- has no restriction on number of parts in version string
- can compare version strings with different number of parts
- 除了不运行任何外部命令
tr
- 对版本字符串中的部分数量没有限制
- 可以比较具有不同部分数量的版本字符串
Note that it's Bash code using array variables.
请注意,它是使用数组变量的 Bash 代码。
compare_versions()
{
local v1=( $(echo "" | tr '.' ' ') )
local v2=( $(echo "" | 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
}
The function returns:
该函数返回:
- 0 if versions are equal (btw: 1.2 == 1.2.0)
- 1 if the 1st version is bigger / newer
- 2 if the 2nd version is bigger / newer
- 0 如果版本相等(顺便说一句:1.2 == 1.2.0)
- 1 如果第一个版本更大/更新
- 2 如果第二个版本更大/更新
However #1 -- it requires one additional function (but function min
is quite usable to have anyway):
然而#1——它需要一个额外的功能(但功能min
无论如何都非常有用):
min()
{
local m=""
for n in "$@"
do
[ "$n" -lt "$m" ] && m="$n"
done
echo "$m"
}
However #2 -- it cannot compare version strings with alpha-numeric parts (though that would not be difficult to add, actually).
但是#2 - 它不能将版本字符串与字母数字部分进行比较(尽管实际上添加起来并不难)。
回答by ianmjones
A much safer option for testing the PHP CLI version is to use PHP's own version_compare
function.
测试 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=;
TEST_VERSION=;
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!";
回答by Six
The following solution should more accurately addresses your exact need. It can be used to test whether the CURRENT
version string falls between MIN
and MAX
. I am assuming that MIN
and MAX
are acceptable version numbers (i.e. MIN <= CURRENT <= MAX
rather than MIN < CURRENT < MAX
).
以下解决方案应该更准确地满足您的确切需求。它可用于测试CURRENT
版本字符串是否介于MIN
和之间MAX
。我假设MIN
和MAX
是可接受的版本号(即MIN <= CURRENT <= MAX
而不是MIN < CURRENT < MAX
)。
# Usage: version MIN CURRENT MAX
version(){
local h t v
[[ = "" || = "" ]] && return 0
v=$(printf '%s\n' "$@" | sort -V)
h=$(head -n1 <<<"$v")
t=$(tail -n1 <<<"$v")
[[ != "$h" && != "$t" ]]
}
For example...
例如...
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
回答by Mark Carver
If you're on Bash 3 with an older version of sort
(lookin at you macOS...), then I created the following helper script you can source in (can also be ran as a command):
如果您使用旧版本的 Bash 3 sort
(看看您的 macOS...),那么我创建了以下帮助脚本,您可以从中获取(也可以作为命令运行):
回答by oracleif
I wrote this inelegant function a while back for a similar problem. vers_limitwill return 0 if arg1 is less than or equal to arg2, non-0 otherwise:
不久前,我为类似的问题编写了这个不雅的函数。 如果 arg1 小于或等于 arg2,则vers_limit将返回 0,否则返回非 0:
vers_limit()
{
VERNEW=
VERLMT=
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"
}
This is not as compact as some of the other solutions here, nor does it provide 3-way status (i.e., less, equal, greater), though I believe one can order the arguments, interpret the pass/fail status, and/or call twice to accomplish any desired result. That said, vers_limitdoes have certain virtues:
这不像这里的其他一些解决方案那么紧凑,也不提供 3 向状态(即,小于、等于、大于),尽管我相信可以对参数进行排序,解释通过/失败状态,和/或调用两次以完成任何所需的结果。也就是说,vers_limit确实具有某些优点:
No calls to external utilities such as sort, awk, gawk, tr, etc.
Handles numeric versions of arbitrary size (up to the shell's limit for integer calculations)
Handles an arbitrary number of "dotted" parts.
无需调用外部实用程序,例如 sort、awk、gawk、tr 等。
处理任意大小的数字版本(达到 shell 对整数计算的限制)
处理任意数量的“虚线”部分。
回答by 5p0ng3b0b
v_min="5.2.15"
v_max="5.3.15"
v_php="$(php -v | head -1 | awk '{print }')"
[ ! "$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
}
This puts v_min
, v_max
and v_php
in version order and tests if v_php
is not in the middle.
这将v_min
, v_max
andv_php
按版本顺序放置并测试是否v_php
不在中间。
回答by grand central
PURE BASH
纯 BASH
I found my way to this page because I had the same problem. The other answers did not satisfy me, so I wrote this function.
So long as the 2 versions have the same number of periods in them this will compare the versions correctly.
It does a c style for loop, setting $i incrementally from 0 to # of numbers in the version string.
for each #:
if new - old is neg we know the first version is newer.
If new - old is pos we know the first version is older.
If new - old is 0 then it is the same and we need to continue checking.
We run false after to set exit status of the function for the case where $1 == $2 the versions are totally identical.
我找到了进入此页面的方式,因为我遇到了同样的问题。其他答案都没有让我满意,所以我写了这个函数。
只要这两个版本中的句点数相同,这就会正确比较版本。
它执行 ac 风格的循环,从 0 到版本字符串中的数字编号递增地设置 $i。
对于每个 #:
如果 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(){
# = new version
# = installed version
IFS='.' read -r -a nver <<< ""
IFS='.' read -r -a iver <<< ""
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
Another method for SH
SH的另一种方法
After I tried to implement my above function on Android I realized that I would not always have bash, so the above function did not work for me. Here is the version I wrote using awk to get around needing bash:
在我尝试在 Android 上实现我的上述功能后,我意识到我不会总是有 bash,所以上述功能对我不起作用。这是我使用 awk 编写的版本以解决需要 bash 的问题:
checkupdate(){
# = new version
# = 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}' <<< "")
while (( "$i" < "$places" )) ;do
npos=$(awk -v pos=$i -F. '{print $pos}' <<< "")
ipos=$(awk -v pos=$i -F. '{print $pos}' <<< "")
case "$(( $npos - $ipos ))" in
-*) return 1 ;;
0) ;;
*) return 0 ;;
esac
i=$((i+1))
false
done
}