How can I sort file names by version numbers?

17,875

Solution 1

Edit: It turns out that Benoit was sort of on the right track and Roland tipped the balance

You simply need to tell sort to consider only field 2 (add ",2"):

find ... | sort --version-sort --field-separator=- --key=2,2

Original Answer: ignore

If none of your filenames contain spaces between the hyphens, you can try this:

find ... | sed 's/.*-\([^-]*\)-.*/\1 \0/;s/[^0-9] /.&/' | sort --version-sort --field-separator=- --key=2 | sed 's/[^ ]* //'

The first sed command makes the lines look like this (I added "10" to show that the sort is numeric):

1.9.a command-1.9a-setup
2.0.c command-2.0c-setup
2.0.a command-2.0a-setup
2.0 command-2.0-setup
10 command-10-setup

The extra dot makes the letter suffixed version number sort after the version number without the suffix. The second sed command removes the prefixed version number from each line.

There are lots of ways this can fail.

Solution 2

If you specify to sort that you only want to consider the second field (-k2) don't complain that it does not consider the third one.

In your case, run sort --version-sort without any other argument, maybe this will suit better.

Solution 3

Looks like this works:

find /data/ -name 'command-*-setup' | sort -t - -V -k 2,2

not with sort but it works:

tree -ivL 1 /data/ | perl -nlE 'say if /\Acommand-[0-9][0-9a-z.]*-setup\z/'

-v: sort the output by version
-i: makes tree not print the indentation lines
-L level: max display depth of the directory tree

Solution 4

Another way to do this is to pad your numbers.

This example pads all numbers to 8 digits. Then, it does a plain alphanumeric sort. Then, it removes the pad.

$ pad() { perl -pe 's/(\d+)/0000000\1/g' | perl -pe 's/0*(\d{8})/\1/g'; }
$ unpad() { perl -pe 's/0*([1-9]\d*|0)/\1/g'; }
$ cat files | pad | sort | unpad
command-1.9a-setup
command-2.0-setup
command-2.0a-setup
command-2.0c-setup
command-10.1-setup

To get some insight into how this works, let's look at the padded sorted result:

$ cat files | pad | sort
command-00000001.00000009a-setup
command-00000002.00000000-setup
command-00000002.00000000a-setup
command-00000002.00000000c-setup
command-00000010.00000001-setup

You'll see that with all the numbers nicely padded to 8 digits, the alphanumeric sort puts the filenames into their desired order.

Solution 5

Old post, but... ls -l --sort=version may be of assistance (although for OP's example the sort is the same as done by ls -l in a RHEL 7.2):

command-1.9a-setup
command-2.0a-setup
command-2.0c-setup
command-2.0-setup

YMMV i guess.

Share:
17,875
sid_com
Author by

sid_com

Updated on June 13, 2022

Comments

  • sid_com
    sid_com almost 2 years

    In the directory "data" are these files:

    command-1.9a-setup
    command-2.0a-setup
    command-2.0c-setup
    command-2.0-setup

    I would like to sort the files to get this result:

    command-1.9a-setup
    command-2.0-setup
    command-2.0a-setup
    command-2.0c-setup

    I tried this

    find /data/ -name 'command-*-setup' | sort --version-sort --field-separator=- -k2 
    

    but the output was

    command-1.9a-setup
    command-2.0a-setup
    command-2.0c-setup
    command-2.0-setup

    The only way I found that gave me my desired output was

    tree -v /data
    

    How could I get with sort the output in the wanted order?