Skip to content

Instantly share code, notes, and snippets.

@Ariel-Rodriguez
Last active June 9, 2023 22:00
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Ariel-Rodriguez/9e3c2163f4644d7a389759b224bfe7f3 to your computer and use it in GitHub Desktop.
Save Ariel-Rodriguez/9e3c2163f4644d7a389759b224bfe7f3 to your computer and use it in GitHub Desktop.
semver compare tool in bash
#!/bin/bash
# THIS IS AN OBSOLETE SCRIPT WITH BUGS. FOR UPDATED VERSIONS PLEASE CHECK https://github.com/Ariel-Rodriguez/sh-semversion-2
###
# semantic version comparition using semver specification http://semver.org/
# This bash script compares pre-releases alphabetically as well
#
# returns 1 when A greater than B
# returns 0 when A equals B
# returns -1 when A lower than B
#
# Usage
# chmod +x semver.sh
# ./semver.sh 1.0.0 v1.0.0-rc.0
# --> 1
#
# Author Ariel Rodriguez
# License MIT
###
semver_compare() {
local version_a version_b pr_a pr_b
# strip word "v" and extract first subset version (x.y.z from x.y.z-foo.n)
version_a=$(echo "${1//v/}" | awk -F'-' '{print $1}')
version_b=$(echo "${2//v/}" | awk -F'-' '{print $1}')
if [ "$version_a" \= "$version_b" ]
then
# check for pre-release
# extract pre-release (-foo.n from x.y.z-foo.n)
pr_a=$(echo "$1" | awk -F'-' '{print $2}')
pr_b=$(echo "$2" | awk -F'-' '{print $2}')
####
# Return 0 when A is equal to B
[ "$pr_a" \= "$pr_b" ] && echo 0 && return 0
####
# Return 1
# Case when A is not pre-release
if [ -z "$pr_a" ]
then
echo 1 && return 0
fi
####
# Case when pre-release A exists and is greater than B's pre-release
# extract numbers -rc.x --> x
number_a=$(echo ${pr_a//[!0-9]/})
number_b=$(echo ${pr_b//[!0-9]/})
[ -z "${number_a}" ] && number_a=0
[ -z "${number_b}" ] && number_b=0
[ "$pr_a" \> "$pr_b" ] && [ -n "$pr_b" ] && [ "$number_a" -gt "$number_b" ] && echo 1 && return 0
####
# Retrun -1 when A is lower than B
echo -1 && return 0
fi
arr_version_a=(${version_a//./ })
arr_version_b=(${version_b//./ })
cursor=0
# Iterate arrays from left to right and find the first difference
while [ "$([ "${arr_version_a[$cursor]}" -eq "${arr_version_b[$cursor]}" ] && [ $cursor -lt ${#arr_version_a[@]} ] && echo true)" == true ]
do
cursor=$((cursor+1))
done
[ "${arr_version_a[$cursor]}" -gt "${arr_version_b[$cursor]}" ] && echo 1 || echo -1
}
[ -n "$1" ] && echo $(semver_compare $1 $2)
#!/bin/bash
# OUTDATED GIST
# PLEASE CHECK https://github.com/Ariel-Rodriguez/sh-semversion-2
# Include semver_compare tool
source ./semver.sh
versions=(
0.0.0 0.0.0 0
0.0.1 0.0.0 1
0.0.0 0.0.1 -1
0.0.1-rc v0.0.1-rc 0
0.0.1-rc.1 v0.0.1-rc 1
0.0.1-rc.0 v0.0.1-rc.2 -1
v0.0.1-rc.0 0.0.1-rc.0 0
v0.0.1 v0.0.1-rc.2 1
v0.0.1 v0.0.1 0
v0.1.0-rc.0 0.1.0-rc.1 -1
0.1.0-rc.1 v0.1.0-rc.1 0
v0.1.0 v0.1.0-rc.1 1
v0.1.0-rc.1 v0.1.0 -1
v0.1.0-rc.9 v0.1.0-rc.10 -1
v0.1.0-alpha v0.1.0-beta -1
v0.1.0-alpha.1 v0.1.0-beta.0 -1
)
_assert_is() {
echo $1 $2 is expected to be $4 $(if [ $3 == $4 ]; then echo "OK"; else echo FAIL got $3; fi)
}
for (( i=0; i<=$(( ${#versions[@]} -3 )); i+=3 ))
do
echo compare $(_assert_is ${versions[i]} ${versions[ i + 1]} $(semver_compare ${versions[i]} ${versions[ i + 1]}) ${versions[i+2]})
done
# compare 0.0.1-rc.0 v0.0.1-rc.2 is expected to be -1 OK
# compare v0.0.1-rc.0 0.0.1-rc.0 is expected to be 0 OK
# compare v0.0.1 v0.0.1-rc.2 is expected to be 1 OK
# compare v0.0.1 v0.0.1 is expected to be 0 OK
# compare v0.1.0-rc.0 0.1.0-rc.1 is expected to be -1 OK
# compare 0.1.0-rc.1 v0.1.0-rc.1 is expected to be 0 OK
# compare v0.1.0 v0.1.0-rc.1 is expected to be 1 OK
# compare v0.1.0-rc.1 v0.1.0 is expected to be -1 OK
@Ariel-Rodriguez
Copy link
Author

Ariel-Rodriguez commented Oct 3, 2017

How to

Download tool

https://gist.githubusercontent.com/Ariel-Rodriguez/9e3c2163f4644d7a389759b224bfe7f3/raw/816453441da56058b73af5812db6217fb71897d2/semver.sh

Add execution permissions

chmod +x semver.sh

Usage

semver.sh 1.0.0 1.0.0-rc.1
--> 1

Test

mkdir .tmp_semver && cd .tmp_semver &&
curl -O https://gist.githubusercontent.com/Ariel-Rodriguez/9e3c2163f4644d7a389759b224bfe7f3/raw/816453441da56058b73af5812db6217fb71897d2/semver.sh && curl -O https://gist.githubusercontent.com/Ariel-Rodriguez/9e3c2163f4644d7a389759b224bfe7f3/raw/816453441da56058b73af5812db6217fb71897d2/test.sh && chmod +x test.sh semver.sh && ./test.sh && cd .. && rm -rf .tmp_semver

@amatsumara
Copy link

amatsumara commented Aug 29, 2018

What about versions like 1.0.5-rc.9 1.0.5-rc.10? For me it

./semver.sh 1.0.5-rc.9 1.0.5-rc.10
1

@Ariel-Rodriguez
Copy link
Author

What about versions like 1.0.5-rc.9 1.0.5-rc.10? For me it

./semver.sh 1.0.5-rc.9 1.0.5-rc.10
1

So sorry for this late response, i have never notice you commented here. Exactly 1 year ago i was traveling and potentially missed it and never checked again this gist.

I have updated the script and also the tests!
Hope this helps for further people.

@hrvoj3e
Copy link

hrvoj3e commented Oct 9, 2019

Great script. Thank you.
An alternative (in dpkg world) would be: https://stackoverflow.com/a/46299983/2450431

dpkg --compare-versions "1.0.5-rc.9" "eq" "1.0.5-rc.10"

And using return code (bash: $?) to check result. Other conditions work also: lt, gt.

@Ariel-Rodriguez
Copy link
Author

Thanks for suggesting. Yes that's an awesome tool. However I intended to find a solution to get rid of dependencies and make it work on OSX natively.

@rfay
Copy link

rfay commented Nov 6, 2020

There's an error in line 54, which I think should be this:

    [ "$pr_a" \> "$pr_b" ] && [ -n "$pr_b" ] && echo 1 && return 0

instead of what's currently there:

    [ "$pr_a" \> "$pr_b" ] && [ -n "$pr_b" ] && [ "$number_a" -gt "$number_b" ] && echo 1 && return 0

The current version will give a -1 when comparing v1.16.0-rc1 to v1.16.0-alpha4 because it ends up looking at the number in the prerelease instead of the pre section itself.

@Ariel-Rodriguez
Copy link
Author

@rfay true that there is a bug when comparing between different prerelease word name. Thanks for reporting it!
Please, be aware your solution breaks other test.
I will take a look on this by this week if possible.

@rfay
Copy link

rfay commented Nov 9, 2020

This is an important-enough script to move into its own repository, where it can have issues and PRs and testing!

@michaelajr
Copy link

michaelajr commented Nov 20, 2020

Great script. Thank you. Would love to see this in a git repo so contributions could be made. Of note, it seems the script does not work with build numbers, as outlined in the semver 2.0 spec. https://semver.org/spec/v2.0.0.html#spec-item-10

Know build numbers can not be compared - but maybe the script should just strip them off?

@Ariel-Rodriguez
Copy link
Author

Wow i didn't expect this script were going to be useful.
I will refactor all this and create a repository. I will focus on next:

  • Create this script 100% POSIX sh compatible. (current one uses AWK and newer Bash, not good 0 portability)
  • I will make it semver 2.0 compatible free of bugs

I will try to make it for today. Thanks for the suggestions and support!

@Ariel-Rodriguez
Copy link
Author

hey @michaelajr @rfay @hrvoj3e @amatsumara

I created repository and script reworked from scratch with better portability and more coverage.

Take a look https://github.com/Ariel-Rodriguez/sh-semversion-2
Thanks.

@rfay
Copy link

rfay commented Nov 23, 2020

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment