25

I have a list of table names, which are out of order. How can I get them in the correct logical order?

$ cat list.txt

TAB1
TAB13
TAB11
TAB19
TAB2
TAB3
TAB16
TAB17
TAB18
TAB9
TAB10
TAB8
TAB12
TAB20

$ cat list.txt | sort -n

TAB1
TAB10
TAB11
TAB12
TAB13
TAB16
TAB17
TAB18
TAB19
TAB2
TAB20
TAB3
TAB8
TAB9

Expected order:

TAB1
TAB2
TAB3
TAB8
TAB9
TAB10
TAB11
TAB12
TAB13
TAB16
TAB17
TAB18
TAB19
TAB20

Any vim short-cuts will also do, I do not necessarily need a separate utility for this.

Lazer
  • 90,700
  • 113
  • 281
  • 364

4 Answers4

41

You can always perform sort with argument -V to sort alphanumeric string..

$ sort -V inputfile > outputfile

$ cat inputfile  
TAB1  
TAB13  
TAB11  
TAB19  
TAB2  
TAB3  
TAB16  
TAB17  
TAB18  
TAB9  
TAB10  
TAB8  
TAB12  
TAB20  

$ cat outputfile  
TAB1  
TAB2  
TAB3  
TAB8  
TAB9  
TAB10  
TAB11  
TAB12  
TAB13  
TAB16  
TAB17  
TAB18  
TAB19  
TAB20  
zishe
  • 10,665
  • 12
  • 64
  • 103
user3682472
  • 431
  • 4
  • 3
  • You should mention that according to the manual, '-V' stands for "--version-sort" and is descrived as "natural sort of (version) numbers within text" -- taken from Ubuntu 20.04 standard repository. So it may not exactly behave as desired. – Till Kolditz Jun 08 '20 at 13:15
26

You need to tell it where your sorting key starts:

sort -n -k1.4 list.txt

Otherwise it starts from the beginning, fails to convert a string to a number and falls back to alphabetical comparison.

cababunga
  • 3,090
  • 15
  • 23
  • 9
    It took me a bit to figure out how -k works: 1 is the field number and 4 is the character. If there was a space (the default field separator) after the T in TAB, then you'd use `sort -n -k2.4 list.txt`. You can use -t to specify custom field separators. `sort -n -tB -k2.1 list.txt` would also work because the B in TAB would divide the fields and we'd sort on the first character of the second field to the end of line. – idbrii Nov 26 '10 at 01:46
7

Since this is tagged as a Vim question, I figured it might be worth mentioning the Vim option (even though I would personally use sort since the data's already in a file). It's simply

:sort n

Since Vim's numeric sort ignores up to the first decimal number, one doesn't need to ignore the "TAB" (:sort can take a pattern to ignore, :sort n /TAB/ would work as well, for example). As usual, :h :sort for more information.

fow
  • 556
  • 3
  • 6
  • Thanks. Didnt know this. I had to go though a lot of pain of deleting the _string_, sort it and put back the _string_. – Jeffrey Jose Nov 27 '10 at 04:52
  • @jeffjose: That only works easily if you have the same string in all cases; it fails when you don't. The worst is when you can't pass the `sort -kX.Yn` shell command a fixed position. I've often had to write a Perl script to deal with those situations, and wish I didn't. Similarly for sorting by the last field when there is a variable number of fields, although there you can just reverse the field order, sort it, then reverse it back again. – tchrist Nov 27 '10 at 14:10
2

You can do this in Perl or any language where sort lets you specify a comparison operator:

sub numcomp() {

 $a =~ /([0-9]*)$/; $aa = $1;
 $b =~ /([0-9]*)$/; $bb = $1;
 $aa <=> $bb;

}

sort numcomp @mylist...

(Don't bother telling me it's baby Perl. I... um, I wrote it that way on purpose so it would be easy to understand.) (Don't bother telling me it's wrong. I... um, I wrote it that way on purpose as an exercise for the reader.)

eshafto
  • 21
  • 1