16

Sometimes I need to add more disk to a database; for that, I need to list the disks to see what disks already exist.

The problem is that the output is always sorted as 1,10,11,12...2,20,21...3 etc.

How can I sort this output the way I want it? A simple sort does not work; I've also tried using sort -t.. -k.. -n.

Example of what I need to sort:

[root@server1 ~]# oracleasm listdisks
DATA1
DATA10
DATA11
DATA12
DATA2
DATA3
DATA4
DATA5
DATA6
DATA7
DATA8
DATA9
FRA1
FRA10
FRA11
FRA2
FRA3
..
OCR1
OCR2
OCR3
....

How I'd like to see the output:

DATA1
DATA2
DATA3
DATA4
DATA5
DATA6
DATA7
DATA8
DATA9
DATA10
DATA11
DATA12
FRA1
FRA2
FRA3
..
..
FRA10
FRA11
..
OCR1
OCR2
OCR3
....
1

2 Answers 2

33

Your best bet is piping to GNU sort, with GNU sort's --version-sort option enabled

so that would be oracleasm listdisks | sort --version-sort

From the info page

--version-sort’
     Sort by version name and number.  It behaves like a standard sort,
     except that each sequence of decimal digits is treated numerically
     as an index/version number.  (*Note Details about version sort::.)

On your input it gives me

DATA1
DATA2
DATA3
DATA4
DATA5
DATA6
DATA7
DATA8
DATA9
DATA10
DATA11
DATA12
FRA1
FRA2
FRA3
FRA10
FRA11
OCR1
OCR2
OCR3
11

If sort --version-sort is not available, split into 2 fields: field 1 = leading non-digits, and field 2 = integer number, and print the fields with TAB between them. Then use sort on 2 TAB-delimited fields, then remove the TAB. Connect by pipes to avoid I/O overhead. Here is an example with a minimal slice of the data from the OP, plus a few additional records:

echo 1 10 2 11 DATA DATA1 DATA10 DATA11 DATA2 FRA FRA1 FRA10 FRA11 FRA2 | \
    xargs -n1 | \
    perl -lne 'print join "\t", /(\D*)(\d*)/' | \
    sort -k1,1 -k2,2n | \
    perl -pe 's/\t//'

Prints:

1
10
11
2
DATA
DATA1
DATA2
DATA10
DATA11
FRA
FRA1
FRA2
FRA10
FRA11

DETAILS:

The perl one-liners use these command line flags:
-e : tells Perl to look for code in-line, instead of in a file.
-n : loop over the input one line at a time, assigning it to $_ by default.
-l : strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-p : same as -n, but also print the line at the end of each loop (eliminates explicit print).

Within the first one-liner, \d is any digit (0-9), and \D is any non-digit. Each of these patterns is repeated 0 or more times (using *). The two patterns are captured using parentheses and returned as a LIST of two fields, which are joined on a TAB and printed.

The second Perl one-liner simply removes the first TAB is finds with nothing (empty string) and prints the line.

To sort on 2 fields, these options are used: -k1,1 : sort on field 1 ASCIIbetically. Then:
-k2,2n : if field 1 is the same, sort on field 2 numerically (-n option).
Note that the field number is repeated twice (e.g., 1,1), to prevent sorting on the rest of the line and limit the sort to just this column number.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .