Ben-xue
Ben-xue

Reputation: 103

how to print per column use awk

the content of a.txt

name age
alice 21
ryan 30

#test.sh
nf=`awk '{print NF; exit}' a.txt`
for((i=1;i<=$nf;i++))
{
    awk '{print $i}' ./a.txt
}

The result I expected is print per column,but the actual result is print all content twice.

my expected output is

name
alice
ryan
age
21
30

actual output is

name age
alice 21
ryan 30
name age
alice 21
ryan 30

Upvotes: 2

Views: 185

Answers (5)

Akshay Hegde
Akshay Hegde

Reputation: 16997

Another approach using gawk without saving inside array, if you prefer saving resource/memory:

awk '
    FNR==1 && ++fcount<NF{
        delete ARGV[fcount]
        ARGV[fcount+1]=FILENAME
        ARGC++
     }
     {
        print $(fcount)
     }
    ' file

Results:

$ cat file 
name age
alice 21
ryan 30


$ awk 'FNR==1 && ++fcount<NF{delete ARGV[fcount];ARGV[fcount+1]=FILENAME;ARGC++}{print $(fcount)}END{for(i in ARGV)print i,ARGV[i]}' file
name
alice
ryan
age
21
30

Upvotes: 2

Ed Morton
Ed Morton

Reputation: 203189

You don't need a shell loop, just 1 call to awk. Using any awk in any shell on every Unix box:

$ cat tst.awk
{
    for (i=1; i<=NF; i++) {
        vals[NR,i] = $i
    }
}
END {
    for (i=1; i<=NF; i++) {
        for (j=1; j<=NR; j++) {
            print vals[j,i]
        }
    }
}

$ awk -f tst.awk a.txt
name
alice
ryan
age
21
30

or, if you prefer, with GNU awk for arrays of arrays:

$ cat tst.awk
{
    vals[NR][1]
    split($0,vals[NR])
}
END {
    for (i=1; i<=NF; i++) {
        for (j=1; j<=NR; j++) {
            print vals[j][i]
        }
    }
}

$ awk -f tst.awk a.txt
name
alice
ryan
age
21
30

Upvotes: 1

RavinderSingh13
RavinderSingh13

Reputation: 133428

This could be done in a single awk itself, please try following; written and tested with your show samples.

awk -v tot="0" '
{
  for(i=1;i<=NF;i++){
    arr[i]=(arr[i]?arr[i] ORS:"")$i
  }
  tot=(tot>NF?tot:NF)
}
END{
  for(i=1;i<=tot;i++){
    print arr[i]
  }
}
' Input_file

Explanation: Adding detailed explanation for above.

awk -v tot="0" '          ##Starting awk program from here, setting tot to 0 here.
{
  for(i=1;i<=NF;i++){     ##Traversing through all fields here.
    arr[i]=(arr[i]?arr[i] ORS:"")$i  ##Creating arr with index of i and keep appending current field value in it.
  }
  tot=(tot>NF?tot:NF)     ##Creating tot by seeing current total number of fields, to get highest NF value here.
}
END{                      ##Starting END block of this program from here.
  for(i=1;i<=tot;i++){    ##Running for loop from i=1 to till tot value here.
    print arr[i]          ##Printing array with index of i here.
  }
}
' Input_file              ##Mentioning Input_file name here.

Upvotes: 2

Carlos Pascual
Carlos Pascual

Reputation: 1126

And withawk without using any array, you can get the expected output:

awk  'NR==FNR{print $1;next}{print $2}' file file
name
alice
ryan
age
21
30

Upvotes: 0

dibery
dibery

Reputation: 3460

Because your $i is only visible to bash, not awk. You need to pass it into your awk script as:

awk -v i=$i '{print $i}' ./a.txt

The -v option allows you to pass variables into awk. i=$i means the $i in your awk script will be $i in your bash.

If other tools are allowed, you can try

cut -f $i a.txt

as a simpler candicate.

Upvotes: 1

Related Questions