3

I am trying to write a quick awk script that converts lines in this format:

AAAA BBBB CCCC DDDD...

to

CCCC AAAA BBBB DDDD...

This is my script:

{ printf "%s %s %s %s", $3, $1, $2, $4 };

This works fine except when the original input line has more than 4 tokens, in which case the 5th and following tokens are not printed.

I checked some answers, like Using awk to print all columns from the nth to the last but they rely on setting variables to "" which seems to cause problems if those variables are reused later.

Is there an easy way to replace $4 by something like "the substring from $4 until the end of the line"?

Community
  • 1
  • 1
Mike Laren
  • 8,028
  • 17
  • 51
  • 70
  • 1
    possible duplicate of [Using awk to print all columns from the nth to the last](http://stackoverflow.com/questions/2961635/using-awk-to-print-all-columns-from-the-nth-to-the-last) – Lynn May 11 '15 at 20:30
  • @Mauris it is a duplicate of that question but all of the answers in that question are cumbersome at best, most add and/or remove spaces undesirably, and they are all wrong for the general case where you just want the end of the record printed as-is. – Ed Morton May 11 '15 at 20:55

4 Answers4

6

In this simple case all you need is:

$ awk '{t=$3; $3=$2; $2=$1; $1=t}1' file
CCCC AAAA BBBB DDDD

but in general with GNU awk for gensub(), \s and \S:

$ awk '{print $3, $1, $2, gensub(/^\s*(\S+\s+){3}/,"","")}' file
CCCC AAAA BBBB DDDD

The gensub() just skips the first 3 fields and will leave all fields and the spaces between fields as-is from that point on:

$ cat file
AAAA BBBB CCCC DDDD    EEEE    FFFF  GGGG

$ awk '{print $3, $1, $2, gensub(/^\s*(\S+\s+){3}/,"","")}' file
CCCC AAAA BBBB DDDD    EEEE    FFFF  GGGG

With other awks you can do the same with match()+substr():

$ awk '{match($0,/^[[:space:]]*([^[:space:]]+[[:space:]]+){3}/); print $3, $1, $2, substr($0,RLENGTH+1)}' file
CCCC AAAA BBBB DDDD    EEEE    FFFF  GGGG

or sub() and a variable:

$ awk '{x=$0; sub(/^[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"",x); print $3, $1, $2, x}' file
CCCC AAAA BBBB DDDD    EEEE    FFFF  GGGG
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
4

It's not necessarily "easy" but it isn't that hard:

{  printf "%s %s %s ", $3, $1, $2;
   for (i=4; i<= NF; ++i) {
       printf "%s ", $i;
   }
   printf "\n";
}

Another way, a bit cleverer:

{ N3 = $3; N2 = $2; N1 = $1;  // capture the fields
  $1 = N3; $2 = N1; $3 = N2;  // reorder the fields
  print;  // print the whole record
}
Ross Presser
  • 6,027
  • 1
  • 34
  • 66
4

use a for loop:

{ printf("%s %s %s", $3, $1, $2); 
  for (i = 4; i <= NF; i++) printf("%s ", $i); 
  printf("\n"); 
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
0

Perl:

perl -lane '($F[0], $F[2]) = ($F[2], $F[0]); print "@F"' file
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • While this answer is probably correct and useful, it is preferred if you include some explanation along with it to explain how it helps to solve the problem. This becomes especially useful in the future, if there is a change (possibly unrelated) that causes it to stop working and users need to understand how it once worked. – Kevin Brown-Silva May 12 '15 at 01:42