antoine
antoine

Reputation: 133

awk- print a column in different rows

I have this simplified table of numbers constituted by 2 columns and several rows. The point here is for each column, take the values and order them in rows so that in each row there are 4 values. This file.txt:

1   2   
1   2   
1   2   
1   2   
1   2   
1   2   
1   2   
1   2   
1   2   

And this is the result that I want:

1    1    1    1
1    1    1    1    
1   

2    2    2    2    
2    2    2    2    
2   

I do the following, creating a script.awk for clarity.

awk -f script.awk file.txt

Where script.awk is

{for (i=1;i<=NF;i++)
     printf "%s" (NR %4==0 ? RS:FS), $i;}

But it does not wok. I know that the following command works for one column, but I do not understand why it does not work for a loop in each column.

{printf "%s" (NR %4==0 ? RS:FS), $1;}

If poossible, I would like an explanation of the sommands used, as I am new to this language. Thanks!

Upvotes: 1

Views: 415

Answers (4)

Tom Fenech
Tom Fenech

Reputation: 74615

The problem with your approach is that awk goes through each line (record) one at a time, so the loop in your block will apply the first field in the first line, the second field, then move on to the next line. This doesn't do what you want, as you need all the elements of the first field before you can do anything.

One option is to build an array of both fields, then print them when you get to the end of the file:

awk 'function p(a) { s=""; for(i=1;i<=NR;++i) s=s a[i] (i%4==0 ? RS:FS); print s }
{ a[NR]=$1; b[NR]=$2 }
END { p(a); p(b) }' file

Upvotes: 1

Kent
Kent

Reputation: 195059

I would post a generic solution:

awk  '{for (i=1;i<=NF;i++) a[i,NR]=$i; }END{
    for(i=1;i<=NF;i++) {
        for(j=1;j<=NR;j++)
           printf "%s%s", a[i,j],(j%4==0||j==NR?"\n":" ");
    } 
}' file

this works for dynamic columns in your input file, e.g.:

kent$ cat f
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4

kent$ awk  '{for (i=1;i<=NF;i++) a[i,NR]=$i; }END{
    for(i=1;i<=NF;i++) {
        for(j=1;j<=NR;j++)
                        printf "%s%s", a[i,j],(j%4==0||j==NR?"\n":" ");
    } 
}' f
1 1 1 1
1 1 1 1
1
2 2 2 2
2 2 2 2
2
3 3 3 3
3 3 3 3
3
4 4 4 4
4 4 4 4
4

you just need to change the 4 to control how many cols in your output. it is also easy to be put as an argument with awk -v cols="$var" ...

Upvotes: 3

Allen.Wong
Allen.Wong

Reputation: 21

Another way, use awk and sort:

cat file|awk '{printf("%s\n%s\n",$1,$2)}'|sort|awk 'BEGIN{ORS=" "}
    {if(NR==1){l=$1};if($1!=l){printf("\n");NR=1};
    print $1;if(NR%4==0){printf("\n")};l=$1}'
1 1 1 1
1 1 1 1
1
2 2 2 2
2 2 2 2
2

Upvotes: 2

anubhava
anubhava

Reputation: 785156

Using awk you can do:

awk '{a[$1]++; b[$2]++} END{for (i=1; i<=a[$1]; i++) printf "%s%s", $1, (i%4)?FS:ORS ; 
    print ""; for (i=1; i<[$2]; i++) printf "%s%s", $2, (i%4)?FS:ORS; print ""}' file
1 1 1 1
1 1 1 1
1
2 2 2 2
2 2 2 2
2

Upvotes: 2

Related Questions