74

I have a list of version numbers, let's say for instance that they are in a file versions.txt

1.2.100.4
1.2.3.4
10.1.2.3
9.1.2.3

I wish to sort them so that they are sorted by version. i.e:

1.2.3.4
1.2.100.4
9.1.2.3    
10.1.2.3

I have tried using various sort commands using the "k" parameters, but do not really understand it well enough to pull it off. Any help would be appreciated.

Ben
  • 6,567
  • 10
  • 42
  • 64

7 Answers7

120

The -V option is the nicest, but I wanted to stay away from installing new/other software since my sort didn’t have that option.

This is the command that worked for me in the end:

sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n test.txt

From comments:

  • To reverse the order: sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr
  • To skip the v prefix: sort -t. -k 1.2,1n -k 2,2n -k 3,3n -k 4,4n
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Ben
  • 6,567
  • 10
  • 42
  • 64
  • This is exactly what I would have expected with just -t. -n, as you'd expect each field to be sorted numerically, with like values in a field defer to the next field to the right. I'd always been frustrated getting sort to behave this way -- its good to know how to do this finally. – dennisjbell Jun 17 '12 at 01:23
  • 4
    Good question. Good answer. To reverse the order, add some 'r's... `sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr` – Stew-au Oct 10 '12 at 05:45
  • 7
    If your version numbers start with a "v" (like v2.7.5) then this will let you sort on the second character of the first group: sort -r -t. -k __1.2,1n__ -k 2,2n -k 3,3n -k 4,4n – Sly_cardinal Feb 09 '15 at 03:15
  • Incredible, still useful today :) – derFunk Sep 27 '17 at 12:40
105
sort -V versions.txt

From man sort:

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

See also Details about version sort.

Indent
  • 4,675
  • 1
  • 19
  • 35
Pik'
  • 6,819
  • 1
  • 28
  • 24
  • 2
    Which versions of `sort` have this? My copy of GNU `sort` (on both OS X and Cent OS) doesn't have the `-V` option. – chrisaycock Dec 20 '10 at 19:49
  • 1
    I'm using Debian Sid, which currently have GNU coreutils 8.5. – Pik' Dec 20 '10 at 19:54
  • 1
    Ah, here it is: http://www.gnu.org/software/coreutils/manual/html_node/Details-about-version-sort.html – chrisaycock Dec 20 '10 at 20:04
  • 3
    The sort -V looks very cool. Unfortunately the sort installed on this box does not support the -V flag. – Ben Dec 20 '10 at 20:47
  • You could install a newest version of sort. What's the OS of this box ? Another solution would be to find a program on the web. Just look for "natural sort". I found at least a PHP script. – Pik' Dec 20 '10 at 21:53
5

BSD does not provide -V by default, so Ben's solution is as close as it gets. For your convenience I post here our version that is able to sort files like <label>-<version>.<ext>:

% ls bla-*.ime | sed -Ee 's/^(.*-)([0-9.]+)(\.ime)$/\2.-1 \1\2\3/'  | sort -t. -n -k1,1 -k2,2 -k3,3 -k4,4 | cut -d\  -f2-
bla-1.ime
bla-1.0.ime
bla-1.0.0.ime
bla-1.1.ime
bla-1.1.29.ime
bla-1.2.3.ime
bla-1.2.29.ime
bla-1.2.30.ime
bla-1.3.ime
bla-1.3.0.ime
bla-1.3.1.ime
bla-1.3.10.ime
bla-1.3.20.ime
bla-1.7.ime
bla-1.11.29.ime
bla-2.3.2.ime
bla-11.2.2.ime

Short explanation:

  • List the files that you want to sort with ls.
  • Find the version number and prefix the line with that.
  • While doing that add -1 to the end to make shorter version number sort first (before .0 even). You could change -1 to 0 if you consider 1.3 to be equivalent to 1.3.0.
  • Sort the lines using Ben's suggested solution on the version number.
  • Chop off the version prefix from the line.

The list now contains a version sorted list of applicable file names. Any additional sorting on the label part is left as an exercise to the reader.

Coroos
  • 371
  • 2
  • 9
  • 4
    Actually, BSD does in fact support `-V` by default: https://www.freebsd.org/cgi/man.cgi?query=sort&sektion=1 since at least 2012. I didn't go back into historical versions to see when it was updated, but Apple is just using ancient copies of the BSD tools. – b4hand Nov 12 '15 at 22:40
  • Thanks! Didn't know, but it makes sense. – Coroos May 27 '16 at 07:50
1

This command:

echo "1.2.100.4,1.2.3.4,10.1.2.3,9.1.2.3" | tr ',' '\n' | sort -V

Gives output:

1.2.3.4
1.2.100.4
9.1.2.3
10.1.2.3
MuertoExcobito
  • 9,741
  • 2
  • 37
  • 78
A. Tovar
  • 11
  • 1
0

In Perl:

sub compare_version_numbers {
   my ($l,$r) = @_;
   my @lx = split("\\.",$l);
   my @rx = split("\\.",$r);
   my $minlen = (@lx < @rx) ? @lx : @rx;
   for (my $i=0; $i < $minlen; $i++) {
      # make numeric by multiplying with 1
      my $l_number = ($lx[$i] * 1);
      my $r_number = ($rx[$i] * 1);
      # compare with spaceship operator
      my $l_vs_r = ($l_number <=> $r_number);
      # return if decision is clear!
      if ($l_vs_r != 0) {
         return $l_vs_r
      }
      # otherwise, next part in array of version numbers
   }
   # if we are here, we could not decide - shortest entry wins!
   return @lx <=> @rx
}
David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
-4
echo "1.2.100.4,1.2.3.4,10.1.2.3,9.1.2.3" | tr ',' '\n' | sort -k1,1n

Output:

1.2.100.4
1.2.3.4
9.1.2.3
10.1.2.3

You should be able to figure out the rest. Good luck

mtk
  • 13,221
  • 16
  • 72
  • 112
mNasim
  • 1
-4
sort -n <versions.txt

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131