34

Suppose I want to compare gcc version to see whether the system has the minimum version installed or not.

To check the gcc version, I executed the following

gcc --version | head -n1 | cut -d" " -f4

The output was

4.8.5

So, I wrote a simple if statement to check this version against some other value

if [ "$(gcc --version | head -n1 | cut -d" " -f4)" -lt 5.0.0 ]; then
    echo "Less than 5.0.0"
else
    echo "Greater than 5.0.0"
fi

But it throws an error:

[: integer expression expected: 4.8.5

I understood my mistake that I was using strings to compare and the -lt requires integer. So, is there any other way to compare the versions?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255

10 Answers10

58

I don't know if it is beautiful, but it is working for every version format I know.

#!/bin/bash
currentver="$(gcc -dumpversion)"
requiredver="5.0.0"
 if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then 
        echo "Greater than or equal to ${requiredver}"
 else
        echo "Less than ${requiredver}"
 fi

(Note: better version by the user 'wildcard': https://unix.stackexchange.com/users/135943/wildcard , removed additional condition)

15

Shorter version, assuming GNU sort:

version_greater_equal()
{
    printf '%s\n%s\n' "$2" "$1" | sort --check=quiet --version-sort
}

version_greater_equal "${gcc_version}" 8.2 || die "need 8.2 or above"

Note that it would report gcc_version=008.002 as not being satisfactory even though it sorts the same as 8.2. That can be alleviated by adding the -s / --stable option to sort.

MarcH
  • 271
  • 3
    (1) This is a minor variation of already-given answers.  You could add value by adding an explanation, which has not yet been posted.  (2) printf '%s\n' is good enough; printf will repeat the format string as needed. – G-Man Says 'Reinstate Monica' Feb 14 '20 at 08:25
  • 1
    I normally prefer editing existing answers but deleting half of them is tricky: others may see value where I don't. Same for verbose explanations. Less is more. – MarcH Feb 18 '20 at 19:56
  • 1
    I know that printf repeats the format string but I the (lack of!) syntax for this is IMHO obscure; so I use this only when required = when the number of arguments is large or variable. – MarcH Feb 18 '20 at 19:59
  • 2
    This is the best solution! – MaXi32 Aug 26 '21 at 06:11
  • What is die ? – buhtz May 16 '23 at 11:52
  • 2
    die is whatever error handling function you want. It's a common name for that. – MarcH May 18 '23 at 07:35
2

Here I give a solution for comparing Unix Kernel versions. And it should work for others such as gcc. I only care for the first 2 version number but you can add another layer of logic. It is one liner and I wrote it in multiple line for understanding.

check_linux_version() {
    version_good=$(uname -r | awk 'BEGIN{ FS="."}; 
    { if ($1 < 4) { print "N"; } 
      else if ($1 == 4) { 
          if ($2 < 4) { print "N"; } 
          else { print "Y"; } 
      } 
      else { print "Y"; }
    }')

    #if [ "$current" \< "$expected" ]; then
    if [ "$version_good" = "N" ]; then
        current=$(uname -r)
        echo current linux version too low
        echo current Linux: $current
        echo required 4.4 minimum
        return 1
    fi
}

You can modify this and use it for gcc version checking.

2

We used to do a lot of version checking in a GNU makefile. We shelled out through the makefile facilities. We had to detect old Binutils and buggy compilers and workaround them on the fly.

The pattern we used was:

#!/usr/bin/env bash

CC=$(command -v gcc)
GREP=$(command -v grep)

# Fixup CC and GREP as needed. It may be needed on AIX, BSDs, and Solaris
if [[ -f "/usr/gnu/bin/grep" ]]; then
    GREP="/usr/gnu/bin/grep"
elif [[ -f "/usr/linux/bin/grep" ]]; then
    GREP="/usr/linux/bin/grep"
elif [[ -f "/usr/xpg4/bin/grep" ]]; then
    GREP="/usr/xpg4/bin/grep"
fi

# Check compiler for GCC 4.8 or later
GCC48_OR_LATER=$("$CXX" -v 2>&1 | "$GREP" -i -c -E "gcc version (4\.[8-9]|[5-9]\.)")
if [[ "$GCC48_OR_LATER" -ne 0 ]];
then
   ...
fi

# Check assembler for GAS 2.19 or later
GAS219_OR_LATER=$("$CXX" -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | "$GREP" -c -E "GNU assembler version (2\.19|2\.[2-9]|[3-9])")
if [[ "$GAS219_OR_LATER" -ne 0 ]];
then
   ...
fi
  • 1
    Nice! This works and is super easy to extend, and very portable! – Brad Parks Apr 22 '20 at 16:19
  • 1
    ""$GREP" -i -c -E "gcc version (4\.[8-9]|[5-9]\.)")" This fails for gcc >=10 (the current version is 11.1.0). I think it might be more future-proof to check if it's lesser than 4.8 instead. But as a simple fix, the following should probably work for any future major versions (e.g.: 999): "$GREP" -i -c -E "gcc version (4\.[8-9]|[5-9]\.|[1-9][0-9]*\.)"). The same issue might affect the GAS check at some point in the future. – kelvin Jun 18 '21 at 11:33
2
function version_compare () {
  function sub_ver () {
    local len=${#1}
    temp=${1%%"."*} && indexOf=`echo ${1%%"."*} | echo ${#temp}`
    echo -e "${1:0:indexOf}"
  }
  function cut_dot () {
    local offset=${#1}
    local length=${#2}
    echo -e "${2:((++offset)):length}"
  }
  if [ -z "$1" ] || [ -z "$2" ]; then
    echo "=" && exit 0
  fi
  local v1=`echo -e "${1}" | tr -d '[[:space:]]'`
  local v2=`echo -e "${2}" | tr -d '[[:space:]]'`
  local v1_sub=`sub_ver $v1`
  local v2_sub=`sub_ver $v2`
  if (( v1_sub > v2_sub )); then
    echo ">"
  elif (( v1_sub < v2_sub )); then
    echo "<"
  else
    version_compare `cut_dot $v1_sub $v1` `cut_dot $v2_sub $v2`
  fi
}

### Usage:

version_compare "1.2.3" "1.2.4"
# Output: <

Credit goes to @Shellman

Xaqron
  • 199
  • 9
1

With lastversion CLI utility you can compare any arbitrary versions, e.g.:

#> lastversion 1.0.0 -gt 0.9.9
#> 1.0.0

Exit code 0 when "greater" condition is satisfied.

1

With zsh, you can use it's numeric ordering (Ordering in reverse) parameter expansion flags:

at_least() [[ $1 = ${${(On)@}[1]} ]]

And then:

if at_least $version 1.12; then
   ...
fi

Note that 1.12-pre1 or 1.12-alpha3 would be considered greater than 1.12. 1.012 sorts the same as 1.12 but since one has to come before the other and we do an equality comparison we'll end up considering it not at_least 1.12.

zsh also comes with an autoloadable is-at-least function aimed for that (intended to compare zsh version numbers but can be used with any version provided they use similar versioning scheme as zsh's):

#! /bin/zsh -
autoload is-at-least
if
  version=$(gcc -dumpfullversion 2> /dev/null) &&
    [[ -n $version ]] &&
    is-at-least 7.2 $version
then
  ...
fi

(we need to check that $version is non-empty as otherwise is-at-least would compare zsh's own version).

Note that -dumpfullversion was added in gcc 7.1.0. -dumpversion has been available for decades but note that it may only give the major version number.

From info gcc dumpversion in the manual from gcc-12.2.0:

'-dumpversion'
Print the compiler version (for example, '3.0', '6.3.0' or '7')--and don't do anything else. This is the compiler version used in filesystem paths and specs. Depending on how the compiler has been configured it can be just a single number (major version), two numbers separated by a dot (major and minor version) or three numbers separated by dots (major, minor and patchlevel version).

'-dumpfullversion'
Print the full compiler version--and don't do anything else. The output is always three numbers separated by dots, major, minor and patchlevel version.

$ gcc --version
gcc (Debian 12.2.0-14) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc -dumpversion 12 $ gcc -dumpfullversion 12.2.0

0

Here's a version of the work by Kemin Zhou above which offers a solution for checking if a kernel version major_version.minor_version is newer than some arbitrary linux kernel of major_version.minor_version, ignoring the rest of the kernel version string.

#!/bin/bash
check_linux_version() {
    maj_ver=$1
    min_ver=$2
    version_good=$(uname -r | awk 'BEGIN{ FS="."};
    { if ($1 < '"${maj_ver}"') { print "N"; }
      else if ($1 == '"${maj_ver}"') {
          if ($2 < '"${min_ver}"') { print "N"; }
          else { print "Y"; }
      }
      else { print "Y"; }
    }')
if [ &quot;$version_good&quot; = &quot;N&quot; ]; then
    return 1
fi

} if check_linux_version 4 16; then echo Y ; else echo N; fi if check_linux_version 5 10; then echo Y ; else echo N; fi if check_linux_version 6 2; then echo Y ; else echo N; fi

I'm using this code here in the Chromebrew project.

0

This is a version that doesn't depend on bash

compare_versions()
{
  var1=$1;
  var2=$2;
  for i in 1 2 3
  do
    part1=$(echo $var1 | cut -d "." -f $i)
    part2=$(echo $var2 | cut -d "." -f $i)
    if [ $part1 -lt $part2 ]
    then
      return 0
    fi
  done
  return 1
}

this can then be run like

if compare_versions `gcc --version` "5.0.0"
then
  echo "Less than 5.0.0"
else
  echo "Greater than or equal to 5.0.0"
fi
AdminBee
  • 22,803
  • What does "edit want take split on . off afterwards and turn back on globbing" mean? If you want to edit your answer please just do so. There is a full edit history so it's easy for people to see what you changed – Chris Davies May 26 '23 at 18:25
0

If the program is Python

The question is not gcc specific though many answers are.* Even the excellent general answers using cut -d" " will fail for older Python because prior to v3.4, python --version wrote to stderr instead of stdout. (!!)

Here is a short bash command to check that your Python is at least v3.8 (or whatever you use), that uses Python to avoid both stderr issues and the gyrations involved with *sh string comparison.

python -c "import sys; sys.version_info < (3,8) and sys.exit(1)" \
    && echo "At least v3.8!" || echo "Old version. Do not use."

Note that the version is a Python tuple like (3, 8, 18, "final", 0) so if you needed at least v3.8.13, you would use (3, 8, 13).

My use case: stop pip if Python is too old, before I accidentally install hundreds of packages for the wrong environment.

$ python -V \
>   && python -c "import sys; sys.version_info < (3,8) and sys.exit(1)" \
>   && echo "Python version OK" \
>   && echo "UPDATING PIP..." \
>   && python -m pip install --upgrade pip \
>   && echo "UPDATING MYPACKAGE..." \
>   && python -m pip install .[dev] \
>   || echo "Old Python or other error."

Python 2.7.18 Old Python or other error.


* The question is: "How to compare a program's version in a shell script?" It gives gcc as an example but is not limited to gcc.

ctwardy
  • 101