Comparing PHP version numbers using Bash?
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.
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
Note that it's Bash code using array variables.
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
}
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
However #1 -- it requires one additional function (but function min
is quite usable to have anyway):
min()
{
local m="$1"
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).
There is possibility for deb-distributions:
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
Here's how to compare versions.
using sort -V
:
function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$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 )
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
)
without 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); }'; }
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.
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')
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
.
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.
EDIT: applied @phk amelioration, as it is noticeably cleverer and remove a subprocess call in the first version using sort
. Thanks.