0

Given tabular output from some program in bash I would like to change order of colums printed. Assume number of columns might vary.

Sample input

Name  Surname   Age
Oli   Aaa       15
Boa   Bbb       25

Expected output

Age  Surname    Name
15   Aaa        Oli
25   Bbb        Boa

What I tried

It seems to me as an easy task when number of columns is known, but I don't know what to do when number of columns is just N. For 3 columns simple AWK script would do:

cat table.txt | awk '{print $3 $2 $1}' > reversed_table.txt

It would be good to achieve this using only POSIX-compliant tools.

M. Twarog
  • 2,418
  • 3
  • 21
  • 39

4 Answers4

4

using only POSIX-compliant tools

awk is posix.

but I don't know what to do when number of columns is just N

Now that's easy. So first, awk is really flexible. The awk '{ i=5; print $i; } will print the 5th column, just like that.

Second you can get the number of columns with NF.

Now, it's just writing a simple for loop and iterating from the NF to first argument and viola!

 awk '{ for(i = NF; i >= 1; --i) printf "%s", $i "\t"; printf "\n" }'

A bit better version without a trailing tabulator:

 awk '{ for(i = NF; i >= 1; --i) printf "%s", $i (i==1 ? "" : OFS); print ""; }'
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 4
    maybe a style choice but why not `i>0` instead. Also, `print ""` should do. However, you'll have a dangling tabs at the end of lines.`printf "%s", $i (i==1?ORS:OFS)` is better. – karakfa Oct 09 '19 at 13:46
  • FWIW I prefer `printf "%s%s", $i, (i>1?OFS:ORS)` - 2 strings so 2 `%s`s and the ORS coming after the OFS both just feel more "right". – Ed Morton Oct 09 '19 at 14:57
1

Here is a Generic solution. Where we have 2 variables named swap1 and swap2, in swap one mention keep mapping with swap2 eg--> we want to exchange 3rd field to 5th field AND 4th field with 6th field. Likewise we can have a number of digits in it(I have considered a scenario where we want to exchange 3rd field to 5th field AND 4th field to 6th field).

swap1 --> 3    4
          |    |
          |    |
          |    |
swap2 --> 5    6

Following is the code:

awk -v swap1="3,4"  -v swap2="5,6" '
BEGIN{  
  num=split(swap1,field1,",")
  num1=split(swap2,field2,",")
  for(i=1;i<=num;i++){
    array1[field1[i]]=i
  }
}
FNR==1{
  print
  next
}
{
  for(i=1;i<=NF;i++){
    if(i in array1){
      tmp=$field1[array1[i]]
      $field1[array1[i]]=$field2[array1[i]]
      $field2[array1[i]]=tmp
    }
  }
}
1
'  Input_file  | column -t
RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93
  • @Ed Morton, Hi Ed sir, I have come up with this generic approach(IMHO :) ), May I ask you your thought sir on same if you wouldn't mind sir, how it looks to you? – RavinderSingh13 Oct 09 '19 at 15:41
1

This might work for you (GNU sed and rev):

sed 's/.*/echo "&" | rev/e;s/\S\+/$(echo "&"|rev)/g;s/.*/echo "&"/e' file

Reverse each line and re-reverse each separate word within the line.

potong
  • 55,640
  • 6
  • 51
  • 83
0
awk '{use = $NF;$NF = "";print use,$2,$1}' OFS="\t" file

Age Surname Name
15  Aaa     Oli
25  Bbb     Boa

I looked into this one: Printing everything except the first field with awk

Claes Wikner
  • 1,457
  • 1
  • 9
  • 8