I am have the toughest time with this and was wondering if anyone can help. I'm trying to compare two versions and output something if a version is old. Here is an example of what I have.
monterey="17612.4.9.1.8"
version=$(defaults read /Applications/Safari.app/Contents/Info.plist CFBundleVersion)
if [ "$version" -ge "$monterey" ] ; then
echo "Up to date"
else
echo "Needs update"
fi
exit 0
What I'd like for it to do is compare the Safari "version" version to the "monterey" version. If Safari is greater than or equal to "Monterey" then echo "Up to date".
But everytime I try to do this I get "integer expression expected" or if I try >= I get "unary operator expected".
How should this be written?
CodePudding user response:
-ge
is used for numerical comparison operations.
This is to determine whether the strings are equal, you should use [ "$version" = "$monterey" ]
CodePudding user response:
17612.4.9.1.8
cannot be considered as an integer. Not even as a number: too many dots. If you want to compare dot-separated versions you must do this one field at a time, starting from the major version number.
One option is to store the fields in an array:
$ m=(${monterey//./ })
$ echo ${m[0]}
17612
$ echo ${#m[@]}
5
m=(${monterey//./ })
replaces all dots in $monterey
by spaces and stores the result in array m
, one space-separated word per cell. ${#m[@]}
expands as the size of the array. So, something like the following should do what you want:
m=(${monterey//./ })
v=(${version//./ })
(( n = ${#v[@]} < ${#m[@]} ? ${#m[@]} : ${#v[@]} ))
for (( i=0; i < n; i )); do
if (( ${v[i]:-0} < ${m[i]:-0} )); then
echo "Needs update"
break
elif (( ${v[i]:-0} > ${m[i]:-0} )); then
echo "Up to date"
break
fi
done
exit 0
(( n = ${#v[@]} < ${#m[@]} ? ${#m[@]} : ${#v[@]} ))
stores the largest array size in variable n
. ${v[i]:-0}
expands as v[i]
if it is set and not the empty string, else as 0
.
But if you can use sort
, instead of plain bash
, you can also use:
l=$(printf '%s\n%s\n' "$monterey" "$version" | sort -nt. | tail -n1)
if [[ $version = $l ]]; then
echo "Up to date"
else
echo "Needs update"
fi
exit 0
The first command sorts the two versions numerically (-n
), using .
as separator (-t.
), keeps only the last (tail -n1
), that is, the largest, and stores it in variable l
. Note that this could not work as you would like if you can have trailing 0
fields: 1.2.0.0
will be considered as larger than 1.2.0
or 1.2
.
As mentioned by @markp-fuso, if your sort
utility supports it (it is a non-POSIX feature found, for instance, in the GNU coreutils sort
), you can also use its -V
option that does exactly what you want:
l=$(printf '%s\n%s\n' "$monterey" "$version" | sort -V | tail -n1)