Comparing version strings in POSIX shell - not as easy as you might think.
The new version of xeno is written as a portable POSIX shell script, and amongst other things, it has some simplifications that require Git 1.7.6+. In order to ensure things work, a simple version check is required.
Of course, nothing is simple…
To start with, we’re stuck with the fact that Git versions are not printed out
in simple x.y.z
format:
$ git --version
git version 1.9.2
and of course, someone may have arrogantly tagged their customized version:
$ git --version
git version 1.8.5.2 (Apple Git-48)
Fortunately, the surrounding junk in both cases is pretty easily dispatched with
a call to cut
:
$ git --version | cut -d ' ' -f3
1.9.2
But now comes the tricky part - comparison. If you can ensure that you version
components will always be 1-digit, i.e. 1.9.2
and not 1.9.12
, then you can
use a simple lexical comparison:
$ test '1.9.2' \< '1.9.3'
$ echo $?
0 # Comparison was true
When you get into the 2-digit range, this become problematic:
$ test '1.9.2' \< '1.9.12'
$ echo $?
1 # Comparison was false
There are a few solutions
available online, but generally they are Bash-specific, and as it turns out,
a bit over-complicated for a simple version requirement comparison. Finally, I
found this entry, which seemed to
provide an elegant solution to version sorting. However, it required the -g
flag, which is a GNU-extension to sort
. Fortunately, the solution was not
horrible, and is quite extensible:
# Check that the version of Git installed is supported
GIT_VERSION=$(git --version | cut -d ' ' -f3)
MIN_GIT_VERSION="1.7.6"
LOWEST_GIT_VERSION=$(printf "$GIT_VERSION\n$MIN_GIT_VERSION" \
| sort -t "." -n -k1,1 -k2,2 -k3,3 -k4,4 \
| head -n 1)
if [ "$LOWEST_GIT_VERSION" != "$MIN_GIT_VERSION" ]; then
echo "error: Git version ($GIT_VERSION) too old, need $MIN_GIT_VERSION+"
exit 1
fi
Basically, we create a list of the minimum required version and the installed version, then sort them and take the lesser of the two, requiring that it is the minimum required version. The important code is:
sort -t "." -n -k1,1 -k2,2 -k3,3 -k4,4
This sorts versions by splitting them on .
characters, and treating the fields
numerically. The -k
flag also allows one to specify comparison type on a
per-field basis, allowing one to customize this strategy based on the format of
the individual version fields, making it extensible beyond simple
semantic versioning.