15

I have the following:

versionNumber=$(sw_vers -productVersion) # Finds version number

versionShort=${versionNumber:0:4}  # Cut string to 1 decimal place for calculation

which works when versions are like this:

10.9.2
10.9.5

but it will not match

10.10.3

as it will return only

10.1

but I want the versionShort to be set to

10.10

I am wanting to match the major version, the first dot and the minor version as above.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
lukemh
  • 5,043
  • 7
  • 30
  • 38

4 Answers4

16

Regexpless solution - cut off last dot and whatever follows it:

versionShort=${versionNumber%.*}
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Careful, this doesn't work when using semver versions with a prerelease suffix like `10.9.5-rc.1` – Chris Jul 12 '22 at 08:07
13

Regexp solution:

[[ $versionNumber =~ ^[0-9]+\.[0-9]+ ]] && echo "${BASH_REMATCH[0]}"

It will always print first two numbers, for example all these:

10.5
10.5.9
10.5.8.2

Will result in 10.5 output. You can also add an else clause to check if something wrong happened (no match found).

Here is a longer version:

if [[ $versionNumber =~ ^[0-9]+\.[0-9]+ ]]; then
    versionShort=${BASH_REMATCH[0]}
else
    echo "Something is wrong with your version" >&2
fi
Aleks-Daniel Jakimenko-A.
  • 10,335
  • 3
  • 41
  • 39
13

I had a similar question, but I needed access to all 3 segments. I did a bit of research and testing and I found this to work well

product_version=$(sw_vers -productVersion)
semver=( ${product_version//./ } )
major="${semver[0]}"
minor="${semver[1]}"
patch="${semver[2]}"
echo "${major}.${minor}.${patch}"

To answer this question directly, you could

product_version=$(sw_vers -productVersion)
semver=( ${product_version//./ } )
major="${semver[0]}"
minor="${semver[1]}"
patch="${semver[2]}"
versionShort="${major}.${minor}"

or you can use less variables

product_version=$(sw_vers -productVersion)
semver=( ${product_version//./ } )
versionShort="${semver[0]}.${semver[1]}"
n8felton
  • 322
  • 2
  • 16
  • 1
    How does "semver=( ${product_version//./ } )" work? I unfortunately get semver="10.11.6//./" when I try this. – Jason Harrison Apr 27 '17 at 18:29
  • 2
    @JasonHarrison the code works by replacing dot "." with space " ", and then taking all the arguments as a bash array. In order for it to work you need to assign the semver text to a variable first. For example, `a="10.11.6"; semver=( ${a//./ } )` – VasiliNovikov Sep 18 '18 at 08:50
9

https://github.com/fsaintjacques/semver-tool https://github.com/fsaintjacques/semver-tool/blob/master/src/semver

SEMVER_REGEX="^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$"


function validate-version {
  local version=$1
  if [[ "$version" =~ $SEMVER_REGEX ]]; then
    # if a second argument is passed, store the result in var named by $2
    if [ "$#" -eq "2" ]; then
      local major=${BASH_REMATCH[1]}
      local minor=${BASH_REMATCH[2]}
      local patch=${BASH_REMATCH[3]}
      local prere=${BASH_REMATCH[4]}
      local build=${BASH_REMATCH[5]}
      eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")"
    else
      echo "$version"
    fi
  else
    error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information."
  fi
}
rhinoceros.xn
  • 803
  • 9
  • 12
  • 5
    Note: this snippet contains code that is licensed under GNU GPL v3: https://github.com/fsaintjacques/semver-tool/blob/master/LICENSE – ItalyPaleAle Jun 04 '20 at 06:19