32

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
  • 66,199
  • 35
  • 114
  • 250
Abhimanyu Saharan
  • 853
  • 4
  • 15
  • 24
  • 3
    There's also a [Stack Overflow question](//stackoverflow.com/questions/4023830/bash-how-compare-two-strings-in-version-format) with a bunch of different suggestions for comparing version strings. – n.st May 29 '16 at 03:31
  • 2
    Much simpler than using pipes: `gcc -dumpversion` – Victor Lamoine Jan 04 '17 at 14:18

9 Answers9

54

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)

Luciano Andress Martini
  • 6,490
  • 4
  • 26
  • 56
14

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.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
MarcH
  • 261
  • 2
  • 5
  • 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.

Kemin Zhou
  • 347
  • 3
  • 8
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
  • 189
  • 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.

Danila Vershinin
  • 1,471
  • 9
  • 11
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
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
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 [ "$version_good" = "N" ]; 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
  • 21,637
  • 21
  • 47
  • 71
  • 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 – roaima May 26 '23 at 18:25