How can I numerically sort a single line of delimited items?
With gawk
(GNU awk
) for the asort()
function:
gawk -v SEP='*' '{ i=0; split($0, arr, SEP); len=asort(arr);
while ( ++i<=len ){ printf("%s%s", i>1?SEP:"", arr[i]) };
print ""
}' infile
replace *
as the field separator in SEP='*'
with your delimiter.
You can also do with the following command in case of a single line (because it's better leave it alone of using shell-loops for text-processing purposes)
tr '.' '\n' <<<"$aline" | sort -n | paste -sd'.' -
replace dots .
with your delimiter.
add -u
to the sort
command above to remove the duplicates.
Notes:
You may need to use -g, --general-numeric-sort
option of sort
instead of -n, --numeric-sort
to handle any class of numbers (integer, float, scientific, Hexadecimal, etc).
$ aline='2e-18,6.01e-17,1.4,-4,0xB000,0xB001,23,-3.e+11'
$ tr ',' '\n' <<<"$aline" |sort -g | paste -sd',' -
-3.e+11,-4,2e-18,6.01e-17,1.4,23,0xB000,0xB001
In awk
no need change, it still will handling those.
Using perl
there's an obvious version; split the data, sort it, join it back up again.
The delimiter needs to be listed twice (once in the split
and once in the join
)
eg for a ,
perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
So
echo 1,100,330,42 | perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
1,42,100,330
Since the split
is a regex, the character may need quoting:
echo 10.1.200.42 | perl -lpi -e '$_=join(".",sort {$a <=> $b} split(/\./))'
1.10.42.200
By using the -a
and -F
options, it's possible to remove the split.
With the -p
loop, as before and set the results to $_
, which will automatically print:
perl -F'/\./' -aple '$_=join(".", sort {$a <=> $b} @F)'
Using Python and a similar idea as in Stephen Harris' answer:
python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' <delmiter>
So something like:
$ cat foo
10.129.3.4
1.1.1.1
4.3.2.1
$ python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' . < foo
3.4.10.129
1.1.1.1
1.2.3.4
Sadly having to do the I/O manually makes this far less elegant than the Perl version.