7

I was looking at this question and I wondered if the following could be done from terminal. I did it in python, just want to see if it could be done from terminal, bash scripting or whatever.

Suppose I have a file that looks like so:

2,4,5,14,9
40,3,5,10,1

could it be sorted like so, by rows (lines)

2,4,5,9,14
1,3,5,10,40

or is it too complicated? I did it using python, I just want to know if it could be done so next time I might not use python. What I have done, is creating lists and sorting them.

2
  • I asked it here because I saw the other question, if it should not be asked here feel free to close it or delete it
    – Lynob
    Apr 13, 2014 at 23:08
  • 5
    I added the missing comma between 5 and 14 in your input example. Based on your output, I assume that was a typo, please clarify if it was not.
    – terdon
    Apr 14, 2014 at 4:51

9 Answers 9

7

This should do about what you're looking for. It will read a file (test.txt) and create an output file (sorted.txt) with the sorted lines in the same order as they appear in the original file.

while read line; do j=$(echo "$line" | sed -e 's/,/\n/g' | sort -n); echo "$j" 
    | sed -e 's/ /,/g' >> sorted.txt; done < test.txt
3
  • did you note the space in the first line 2,4,5 14,9? Apr 14, 2014 at 4:36
  • bad usage of cat! To operate on each line, do while read line; do ...; done < test.txt Apr 14, 2014 at 7:30
  • @FredrikPhil: Thank you. I have edited my answer.
    – jkt123
    Apr 14, 2014 at 9:11
5

Here's another Perl approach:

perl -F, -lane 'print join ",",sort {$a<=>$b} @F' file.txt

And another shell/coreutils one (though personally, I prefer steeldriver's excellent answer which uses the same idea):

while read line; do 
    tr , $'\n' < <(printf -- "%s" "$line") | sort -g | tr $'\n' , | sed 's/,$/\n/';
done < file.txt
3
  • 1
    Nice solution, I up-vote immediately Apr 14, 2014 at 5:41
  • As is yours and as did I :)
    – terdon
    Apr 14, 2014 at 5:45
  • I hate it when I receive too many good answers :) and when you have to pick one randomly, it's like asking myself "what do I like more, chocolate bars or icecream?"
    – Lynob
    Apr 14, 2014 at 21:28
4

The only real difficulty in doing what you want using command-line scripting is that the available sort function expects to sort lines within a file, rather than fields within a line. To work around that, you could replace the field delimiters, line-by-line, with newlines prior to the sort function, then replace the newlines with delimiters again in each line after sorting.

There are a number of available text processing utilities which would allow you to do that (including sed, awk, or even the simple tr) however the bash shell itself can do a lot nowadays. Assuming you meant for the input to be comma-delimited (you have a mix of commas and whitespace delimiters in your example) you could do:

while read line; do 
  sorted=$(sort -g -- <<< "${line//,/$'\n'}")
  printf -- "${sorted//$'\n'/,}\n"
done < file

If you do need to handle space delimiters in the input, then you can add them as part of a character list [, ] for the input substring replacement:

while read line; do 
  sorted=$(sort -g -- <<< "${line//[, ]/$'\n'}")
  printf -- "${sorted//$'\n'/,}\n"
done < file

(note that the output remains strictly comma-delimited though).

4

Here comes an awk solution,

while read -r line; do (echo $line | awk '{ n=split($1,a,","); asort(a); for(i=0;i<=n;i++) { print a[i];}}') | xargs | sed -e 's/ /,/g'; done < text.txt

Explanation:

  • awk '{ n=split($1,a,","); asort(a); for(i=0;i<=n;i++) { print a[i];}}'

    awk Splits the field 1 according to the delimiter , and store each value into the array a finally the top position is stored into the variable n. Next asort(a) function sort down the array values.Then the for loop inside the awk command prints the sorted values in the format of record by record.

  • xargs | sed -e 's/ /,/g'

    xargs combines all the rows into a single row.

  • sed -e 's/ /,/g'

    It replaces the spaces with commas ,

  • while read -r line;

    All the above awk,xargs,sed functions are done line by line with the help of while loop.

3

python -c "your code here" input.txt

Or, if your program has multiple lines and eval() is too slow:

python yourprogram.py input.txt

This oneliner works:

python -c "for l in open('input.txt'):print(','.join(sorted(l.strip().split(','), key=int)))"
3
  • The OP is not looking for a python-based solution.
    – jobin
    Apr 14, 2014 at 16:43
  • Yes he is: "just want to see if it could be done from terminal, bash scripting or whatever." Apr 14, 2014 at 17:01
  • Lol, you nailed it, then!
    – jobin
    Apr 14, 2014 at 17:04
3

Using tr and sed

Similar in spirit to what jkt123 wrote:

while read i; do tr , \\n <<< $i | sort -n | tr \\n , | sed 's/,$//'; done

It iterates over the lines, and for each line replaces commas by newlines, sorts the resulting lines numerically, then turns newlines back to commas. The main problem is that this will end the whole result with a comma not a newline, which is addressed by that last sed. It strips the last comma and also implicitely adds a newline to its output.

Using bash arrays

Here is a different approach to deal with this “join with commas” issue:

while read i; do j=( $(tr , \\n <<< $i | sort -n) ); ( IFS=,; echo "${j[*]}"; ); done

Or written with line wraps:

while read i
do
   j=( $(tr , \\n <<< $i | sort -n) )
   ( IFS=,; echo "${j[*]}"; )
done

It uses a bash array to store the result of the sorted row, and makes use of the way bash expands arrays into string literals using the IFS special variable.

2

You can do this easily using perl

consider you have file text.txt

cat text.txt

1,5,7,8,3,6
10,34,67,1,2,0,5

Now the following perl code will sort the rows in text.txt

 perl -i -e 'while(<>){chomp; my @sorted = sort { $a <=> $b } split(",", $_); print join (",", @sorted); print "\n"}' text.txt

After sorting

cat text.txt

1,3,5,6,7,8
0,1,2,5,10,34,67
1
  • 2
    You could shorten this significantly with perl -i -lne 'print join (",", sort {$a <=> $b} split(",", $_));' text.txt. The -n flag is the same as while(<>){} and the -l adds a \n to every print call and also activates implicit chomp().
    – terdon
    Apr 14, 2014 at 6:00
2

The shortest solution using a perl oneliner:

perl -ne 'print join(",", sort { $a <=> $b } split(/,|\n/))."\n"' text.txt
2
  • Heh, I'd left my answer open for a while and hadn't seen this, but not the shortest :P
    – terdon
    Apr 14, 2014 at 5:39
  • 1
    Je m'incline ;-) Apr 14, 2014 at 5:41
1

I needed this functionality too, and a solution I found is as follows (assuming data are in test file):

cat test | while read line; do echo $(echo $line | datamash -t',' transpose |sort -g| datamash -t',' transpose); done

You must log in to answer this question.

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