85

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.

1

7 Answers 7

126

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
Sign up to request clarification or add additional context in comments.

4 Comments

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.
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
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
Incredible, still useful today :)
120
sort -V versions.txt

From man sort:

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

See also Details about version sort.

5 Comments

Which versions of sort have this? My copy of GNU sort (on both OS X and Cent OS) doesn't have the -V option.
I'm using Debian Sid, which currently have GNU coreutils 8.5.
The sort -V looks very cool. Unfortunately the sort installed on this box does not support the -V flag.
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.
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.

2 Comments

Actually, BSD does in fact support -V by default: 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.
Thanks! Didn't know, but it makes sense.
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

1 Comment

This should be more readable: 1.2.3.4 1.2.100.4 9.1.2.3 10.1.2.3
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
}

Comments

-4
sort -n <versions.txt

1 Comment

No, what he wants is a natural sort, not a numeric sort.
-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

1 Comment

Why is 100 less than 3 here? Version 1.2.100.4 should come after 1.2.3.4

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.