ehime ehime - 3 months ago 32
Bash Question

BASH comparing version numbers

Hey guys 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?

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

Answer

Here's how to compare versions.

using sort -V:

function version_gt() { test "$(echo "$@" | tr " " "\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 with man 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.

Comments