-1

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 every time I try to do this I get "integer expression expected" or if I try >= I get "unary operator expected".

How should this be written?

Michael M.
  • 10,486
  • 9
  • 18
  • 34
Lucy_Gucy
  • 1
  • 1
  • 1
    consider updating the question to show us what's in the `version` variable, and explain what you think the expected output should be; version string comparisons typically requires comparing each tuple or simply feeding the 2 version strings to `sort -V`, you can then compare the 1st (or 2nd) line to what you'd expect to see to let you know which string is 'greater'; eg, `printf "%s\n" "${monterey}" "${version}" | sort -V` – markp-fuso Feb 16 '22 at 01:54

2 Answers2

1

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)
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
-1

-ge is used for numerical comparison operations.

This is to determine whether the strings are equal, you should use [ "$version" = "$monterey" ]

过过招
  • 3,722
  • 2
  • 4
  • 11