Skip to content

/commands/semver/sort-version-numbers

Sort version numbers correctly: sort -V vs semver

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

Plain sort orders version numbers as text, so 1.10.0 comes before 1.9.3. Use sort -V (GNU coreutils version sort) for numeric order. sort -V does not know Semantic Versioning pre-release rules and puts 1.2.0 before 1.2.0-alpha; when the list contains pre-releases, sort with a SemVer tool such as npx semver, which follows the SemVer precedence rules.

Compare the three orders

bash
printf '1.10.0\n1.2.0\n1.9.3\n1.2.0-beta.2\n1.2.0-beta.10\n1.2.0-alpha\n' > versions.txt
echo "sort:     $(sort versions.txt | tr '\n' ' ')"
echo "sort -V:  $(sort -V versions.txt | tr '\n' ' ')"
echo "semver:   $(npx -y semver@7 $(cat versions.txt) | tr '\n' ' ')"

Output

sort:     1.10.0 1.2.0 1.2.0-alpha 1.2.0-beta.10 1.2.0-beta.2 1.9.3
sort -V:  1.2.0 1.2.0-alpha 1.2.0-beta.2 1.2.0-beta.10 1.9.3 1.10.0
semver:   1.2.0-alpha 1.2.0-beta.2 1.2.0-beta.10 1.2.0 1.9.3 1.10.0
  • sort compares character by character: 1.1 sorts before 1.2 and 1.9, so 1.10.0 is first.
  • sort -V compares the numbers as numbers and beta.10 after beta.2, but treats the shorter 1.2.0 as smaller than 1.2.0-alpha.
  • semver puts every pre-release before its release (1.2.0-alpha < 1.2.0-beta.2 < 1.2.0-beta.10 < 1.2.0), as the specification says.

Latest version, filtered by range

bash
npx -y semver@7 -r ">=1.2.0 <2.0.0" 1.10.0 1.2.0 2.0.0-rc.1 0.9.0 | tail -1
printf 'v1.2.0\nv1.10.0\nv1.9.3\n' | sort -V | tail -1

Output

1.10.0
v1.10.0

semver -r prints only versions that satisfy the range, sorted ascending; tail -1 keeps the highest. Pre-releases are excluded unless you add -p (--include-prerelease).

Tools at a glance

ToolNumeric orderPre-releasesNotes
sortnonotext order; only correct when all parts have the same width
sort -Vyesno (1.2.0 before 1.2.0-alpha)GNU coreutils; handles prefixes like app-1.10.0.tar.gz
sort -t. -k1,1n -k2,2n -k3,3nyesnoPOSIX; fails on prefixes and suffixes
npx semveryesyes (SemVer rules)drops strings that are not valid SemVer; -c coerces them
git tag --sort=version:refnameyesconfigurableversionsort.suffix sets where suffixes like -rc go

File names, prefixes and invalid versions

bash
printf 'app-1.10.0.tar.gz\napp-1.9.3.tar.gz\napp-1.2.0.tar.gz\napp-2.0.0.tar.gz\n' > files.txt
echo "newest first: $(sort -rV files.txt | tr '\n' ' ')"
echo "mixed v:      $(printf 'v1.2.0\n1.2.0\nv1.10.0\n' | sort -V | tr '\n' ' ')"
echo "semver:       $(npx -y semver@7 v1.2.0 1.10.0 1.2 | tr '\n' ' ')"
echo "semver -c:    $(npx -y semver@7 -c 1.2 v1.10.0 release-2.0 | tr '\n' ' ')"

Output

newest first: app-2.0.0.tar.gz app-1.10.0.tar.gz app-1.9.3.tar.gz app-1.2.0.tar.gz
mixed v:      1.2.0 v1.2.0 v1.10.0
semver:       1.2.0 1.10.0
semver -c:    1.2.0 1.10.0 2.0.0
  • sort -rV reverses version order, so the newest archive comes first.
  • With a mixed v prefix, sort -V keeps the plain 1.2.0 apart from the v versions; normalise the prefix first.
  • semver strips a leading v but silently drops 1.2, which is not valid SemVer. With -c it coerces each string to the first version-like part it finds.

Git tags and other systems

bash
git tag --sort=version:refname        # Git's own version sort, ascending
git tag --sort=-version:refname | head -1   # newest tag
sort -t. -k1,1n -k2,2n -k3,3n file    # POSIX sort without -V (no pre-releases)

Where sort -V is not available, use GNU coreutils (gsort -V from Homebrew on macOS) or the POSIX key form above. In PowerShell, cast to [version]: '1.10.0','1.9.3' | Sort-Object { [version]$_ } (this type does not accept pre-release suffixes).

FAQ

How do I get the newest version first?

Add -r to version sort (sort -rV) and take the first line with head -1. With semver, keep the ascending output and use tail -1.

Why does npx semver print nothing for 1.2?

1.2 is not a valid SemVer version (it needs three numbers), so semver drops it and exits with 1 when nothing valid is left. Add -c to coerce such strings into 1.2.0 first.

Does sort -V handle a leading v?

Only if every line has it. Mixed lists put 1.2.0 before v1.2.0 because the comparison starts with the characters; strip the prefix first, for example with sed 's/^v//'.

Sources