1

I'm trying to sort the numbers on each line of a file individually. The numbers within one line are separated by tabs. (I used spaces but they're actually tabs.)

For example, for the following input

5 8 7 6 
1 5 6 8
8 9 7 1

the desired output would be:

5 6 7 8
1 5 6 7
1 7 8 9

My attempt so far is:

let i=1
while read line
do
    echo "$line" | tr "    " "\n" | sort -g
    cut -f $i fileName | paste -s >> tempFile$$
    ((++i))
done < fileName
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
Solomon
  • 169
  • 1
  • 4
  • 10
  • Somebody proposed http://stackoverflow.com/questions/25062169/using-bash-to-sort-data-horizontally as a duplicate, and it has one answer which actually solves this problem, but that question is really about shuffling entire columns so the first line is sorted. – tripleee Jul 01 '15 at 06:54

5 Answers5

3

This is the best I got - I'm sure it can be done in 6 characters with awk/sed/perl:

while read line
do
  echo $(printf "%d\n" $line | sort -n) | tr ' ' \\t >> another-file.txt
done < my-input-file.txt
Sean Bright
  • 118,630
  • 17
  • 138
  • 146
  • Thanks! That's what I was looking for. Instead of echoing the line, would it be possible to sort the numbers in each row and put it back into the variable line so I can put it in another file? – Solomon Jun 30 '15 at 22:19
  • I think my edit addresses your question, but let me know. – Sean Bright Jun 30 '15 at 22:23
  • Yeah this was exactly what I wanted and it works perfectly now. My only concern is that it outputs a 0 on the last line and I'm not sure why it does that. I can just remove the last line after doing that. Thanks a lot. – Solomon Jun 30 '15 at 22:39
  • Pay close attention to the `printf` trick employed. In bash, `printf` with a single conversion specifier (i.e. `%d\n`) will print all numbers in line on separate lines allowing `sort -n` to sort them, while `echo $(command substitution)` will print the result in its original format on one line. This works with **indexed arrays** as well (e.g. `printf "%d\n" ${array[@]}`) (you can use `%s` as well) – David C. Rankin Jul 01 '15 at 00:50
1

Using a few features that are specific to GNU awk:

$ awk 'BEGIN{ PROCINFO["sorted_in"] = "@ind_num_asc" }
      { delete(a); n = 0; for (i=1;i<=NF;++i) a[$i]; 
        for (i in a) printf "%s%s", i, (++n<NF?FS:RS) }' file
5 6 7 8
1 5 6 8
1 7 8 9

Each field is set as a key in the array a. In GNU awk it is possible to specify the order in which the for (i in a) loop traverses the array - here, I've set it to do so in ascending numerical order.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
0

Here is a bash script that can do it. It takes a filename argument or reads stdin, was tested on CentOS and assumes IFS=$' \t\n'.

#!/bin/bash

if [ "$1" ] ; then exec < "$1" ; fi

cat - | while read line
do   
  set $line
  echo $(for var in "$@"; do echo $var; done | sort -n) | tr " " "\t" 
done

If you want to put the output in another file run it as:

cat input_file | sorting_script > another_file

or

sorting_script input_file > another file

0

Consider using perl for this:

perl -ape '@F=sort @F;$_="@F\n"' input.txt

Here -a turns on automatic field splitting (like awk does) into the array @F, -p makes it execute the script for each line and print $_ each time, and -e specifies the script directly on the command line.

Not quite 6 characters, I'm afraid, Sean.

This should have been simple in awk, but it doen't quite have the features needed. If there had been an array $@ corresponding to the fields $1, $2, etc., then the solution would have been awk '{asort $@}' input.txt, but sadly no such array exits. The loops required to move the fields into an array and out of it again make it longer than the bash version:

awk '{for(i=1;i<=NF;i++)a[i]=$i;asort(a);for(i=1;i<=NF;i++)printf("%s ",a[i]);printf("\n")}' input.txt

So awk isn't the right tool for the job here. It's also a bit odd that sort itself doesn't have a switch to control its sorting direction.

amaurea
  • 4,950
  • 26
  • 35
0

Using awk

$ cat file
5 8 7 6 
1 5 6 8
8 9 7 1

$ awk '{c=1;while(c!=""){c=""; for(i=1;i<NF;i++){n=i+1; if($i>$n){c=$i;$i=$n;$n=c}}}}1' file
5 6 7 8
1 5 6 8
1 7 8 9

Better Readable version

awk '{
      c=1
      while(c!="")
      {
         c="" 
         for(i=1;i<NF;i++)
         {
            n=i+1
            if($i>$n)
            {
              c=$i
              $i=$n
              $n=c
            }
         }
      }
    }1
   ' file

If you have ksh, you may try this

#!/usr/bin/env ksh
while read line ; do 
      set -s +A  cols $line 
      echo ${cols[*]}
done < "input_file"

Test

[akshay@localhost tmp]$ cat test.ksh
#!/usr/bin/env ksh

cat <<EOF | while read line ; do set -s +A  cols $line; echo ${cols[*]};done
5 8 7 6 
1 5 6 8
8 9 7 1
EOF

[akshay@localhost tmp]$ ksh test.ksh
5 6 7 8
1 5 6 8
1 7 8 9
Akshay Hegde
  • 16,536
  • 2
  • 22
  • 36