How can I sort a list with major.minor.patch level and sometimes rc correctly?

GNU sort has -V that can mostly deal with a list like that (details):

 -V, --version-sort
        natural sort of (version) numbers within text

$ cat vers
release-5.0.19
release-5.0.19~pre1
release-5.0.19-bigbugfix
release-5.0.2
release-5.0.20
$ sort -V vers
release-5.0.2
release-5.0.19~pre1
release-5.0.19
release-5.0.19-bigbugfix
release-5.0.20

However, those .rc* versions could be a bit of a problem, since they probably should be sorted before the corresponding non-rc version, if there happened to be both, that is. Some versioning systems (like Debian's), use suffixes starting with a tilde (~) to mark pre-releases, and they sort before the version without a suffix, which sorts before versions with other suffixes. Apparently this is supported by at least the sort on my system, as shown above (sort (GNU coreutils) 8.23).

To sort the example list, you could use the following:

perl -pe 's/\.(?=rc)/~/' < versions.txt | sort -V | perl -pe 's/~/./' > versions-sorted.txt

Check out sort -V:

   -V, --version-sort
          natural sort of (version) numbers within text

Version numbers are complicated beasts, with very few standards governing the alphabetic portions, but try this on your actual data and see if it's sufficient.


This is can be done as one line, but split into multiple lines (at the pipes) here for readability, and handles the rc's, too.

If you don't have a -V option for your sort, or even if you do, you'll need to deal with the occasional rc's:

cat versionlist |
sed -e "s/release-//" -e "s/rc//" |
sort -t. -n -k1,1 -k2,2 -k3,3 -k4,4 |
sed -r -e "s/([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)/\1.\2.\3.rc\4/" -e "s/^/release-/"

The first sed strips the non-numeric characters
The sort uses a . delimiter (-t.), numeric sort (-n), and keys (-k )
The final sed puts the non-numeric characters back in place.