Skip to content

/commands/semver/compare-two-versions-bash

Compare two version numbers in bash (is version A greater than B?)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
SemVer
versions
GNU coreutils 7+ (sort -V), semver (npm) 7, python-semver 3
os
linux, macos, windows

The portable way to compare two version numbers in a bash script is GNU version sort: printf '%s\n' "$a" "$b" | sort -V -C exits with 0 when $a is lower than or equal to $b, because -C checks that the input is already in order. It handles 1.10.0 versus 1.9.0 correctly. For versions with pre-release tags (1.2.0-rc.1), sort -V gets the order wrong; use a SemVer-aware tool such as npx semver or Python's semver package.

Helper functions with sort -V

bash
version_le() { printf '%s\n' "$1" "$2" | sort -V -C; }
version_ge() { version_le "$2" "$1"; }
version_lt() { [ "$1" != "$2" ] && version_le "$1" "$2"; }
for pair in "1.9.0 1.10.0" "2.0.0 2.0.0" "3.1 3.0.9" "1.2.0-rc.1 1.2.0"; do
  set -- $pair
  version_lt "$1" "$2" && echo "$1 <  $2" || { version_le "$2" "$1" && [ "$1" != "$2" ] && echo "$1 >  $2" || echo "$1 == $2"; }
done
echo "--- string comparison gets it wrong"
[[ 1.10.0 > 1.9.0 ]] && echo "1.10.0 > 1.9.0" || echo "string compare says 1.10.0 < 1.9.0"

Output

1.9.0 <  1.10.0
2.0.0 == 2.0.0
3.1 >  3.0.9
1.2.0-rc.1 >  1.2.0
--- string comparison gets it wrong
string compare says 1.10.0 < 1.9.0

The last pair shows the limit: by SemVer rules 1.2.0-rc.1 is lower than 1.2.0, but sort -V puts the shorter string first and reports the opposite.

SemVer-aware comparison

bash
a=1.2.0-rc.1 b=1.2.0
echo "npx semver -r:    $(npx -y semver@7 -r "<$b" "$a" >/dev/null && echo "$a < $b" || echo "no match")"
echo "npx semver -p -r: $(npx -y semver@7 -p -r "<$b" "$a" >/dev/null && echo "$a < $b" || echo "no match")"
uv run -q --no-project --python 3.13 --with semver==3.0.4 python -c '
import sys, semver
a, b = sys.argv[1], sys.argv[2]
print("Version.parse(a).compare(b):", semver.Version.parse(a).compare(b))
print("Version.parse(a) < b:", semver.Version.parse(a) < b)' "$a" "$b"

Output

npx semver -r:    no match
npx semver -p -r: 1.2.0-rc.1 < 1.2.0
Version.parse(a).compare(b): -1
Version.parse(a) < b: True

npx semver -r RANGE VERSION prints the version and exits with 0 when it satisfies the range, so -r "<$b" works as a less-than test. By default, though, a pre-release version matches a range only if one of its comparators has a pre-release tag on the same MAJOR.MINOR.PATCH; <1.2.0 has none, so the first test reports no match. -p (--include-prerelease) turns that rule off and gives the SemVer answer. Version.compare returns -1, 0 or 1, and Version objects also support <, == and > against strings.

Options

MethodCommandHandles pre-releases?
GNU sortprintf "%s\n" A B | sort -V -CNo; fine for plain X.Y.Z and X.Y.
npm semvernpx semver -p -r "<B" AYes with -p; without it, ranges skip most pre-releases.
Node.jssemver.gt(a, b), semver.compare(a, b)Yes.
Python semversemver.Version.parse(a).compare(b)Yes; raises ValueError for invalid versions.
Python packagingVersion(a) < Version(b)PEP 440 rules, for Python package versions.
dpkg (Debian)dpkg --compare-versions A lt BDebian rules: a ~ suffix sorts before the release.

Pitfalls

  • Invalid input fails differently per tool. sort -V compares anything; semver.Version.parse("1.2") raises ValueError: 1.2 is not valid SemVer string; npx semver prints nothing and exits with 1.
  • Strip a leading v from git tags with ${tag#v} before comparing, unless every value has it.
  • Build metadata is ignored for precedence. 1.0.0+a and 1.0.0+b compare equal in SemVer tools, but differ as strings and in sort -V.
  • set -- $pair splits on spaces. It is used here to unpack test pairs; in real scripts pass the two versions as separate arguments.

FAQ

Why not compare versions with [[ $a > $b ]]?

Inside [[ ]], > compares strings character by character, so 1.10.0 is less than 1.9.0. The -gt operator compares integers only and fails on dotted versions. Use sort -V or a SemVer tool.

Does sort -V work on macOS?

macOS ships a BSD-derived sort, not GNU sort. Test that sort -V -C behaves as expected on the macOS versions you support, or install GNU coreutils (gsort) or use npx semver when the script must run on any Mac.

How do I check a minimum version of a tool in a script?

Extract the number from the tool's --version output first (for example with grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1), then run the version_ge function above against the minimum you need.

Sources