what is the meaning of a[FNR]=a[FNR]?a[FNR]","$0:$0 in awk?

Viewed 99

I know the ? is for something like

(condition) ? statement-1: statement-2

but, in this case I am not understanding how a[FNR]=a[FNR] is working, when is this false?

awk '{a[FNR]=a[FNR] ? a[FNR]","$0 : $0} END{for(i=1;i<=FNR;i++)print a[i]}' *.csv
4 Answers
a[FNR]=a[FNR] ? a[FNR]","$0 : $0

Here ? is a ternary operator. Where condition is just a[FNR]. Where a is an associative array.

It means if a[FNR] is not empty and non-zero then set a[FNR] = a[FNR] "," $0 expression otherwise set a[FNR] = $0.

In other words it is equivalent of:

if (a[FNR]) {
   a[FNR] = a[FNR] "," $0
} else {
   a[FNR] = $0
}

Correct approach is to use it this way as Ed rightly suggest in comments:

a[FNR] = (FNR in a ? a[FNR] "," : "") $0

Following detailed explanation may help you here on same. This is only for explanation purposes.

awk '                                   ##Starting awk program from here.
{
  a[FNR]=a[FNR] ? a[FNR]","$0 : $0      ##Creating an array named a with index of current line number FNR.
                                        ##It is keep concatenating same line number index array value to itself to get all same line number values in a single array item.
}
END{                                    ##Starting END block of this program from here.
  for(i=1;i<=FNR;i++){                  ##Running for loop till value of FNR here.
    print a[i]                          ##Printing array a value with index of variable i here.
  }
}' *.csv                                ##Passing all csv files here.

Why we usually do this(since we are passing lot of .csv files into the awk program and same line numbers values to be stored on same index value of array we are using this), these are ternary operators(? and :), its format is like condition?value when condition is TRUE: value when condition is false. Basically if a condition is TRUE then after ? value will be assigned, if condition is false then value after : will be assigned.

The missing parentheses in the conditional operator sure confuse a lot compared to when applied:

$ awk '
BEGIN {
    a="a"                 # some values for a and b
    b="b"
    a=(a ? a "," b : b)   # properly parenthesized ternary operator
    print a
}'

Some test cases for varying values of a (or a[FNR] in your sample) to consider:

$ awk 'BEGIN{a="a";b="b";a=a?a","b:b;print a}'
a,b
$ awk 'BEGIN{a="";b="b";a=a?a","b:b;print a}'
b
$ awk 'BEGIN{a="0";b="b";a=a?a","b:b;print a}'
0,b
$ awk 'BEGIN{a=0;b="b";a=a?a","b:b;print a}'
b

Basically a gets appended a comma and the value of b when a is non-empty and numerically not zero.

PS. you should probably monitor the value of b for the comma control (left as an exercise), who would like the output:

a,

Relevant piece of documentation for understanding effect of

a[FNR]=a[FNR] ? a[FNR]","$0 : $0

is Operator Precedence (How Operators Nest) which states which operator have higher precedence, thus allowing to answer where ( and ) can be put without changing logic. In your example you have assignment, contatenation and ternary. String contatenation has highest precedence of all enumerated, ternary is intermediate and assignment lowest, so after applying that:

a[FNR]=(a[FNR] ? (a[FNR]","$0) : $0)

Documentation prompts code writers to use ( and ) to avoid possible confusion

it is wise to always use parentheses whenever there is an unusual combination of operators, because other people who read the program may not remember what the precedence is in this case. Even experienced programmers occasionally forget the exact rules, which leads to mistakes. Explicit parentheses help prevent any such mistakes.

Note that need of providing operator precedence is not limited to AWK language, but all languages using infix notation, i.e. most of commonly used programming languages (languages exploiting Reverse Polish Notation for example FORTH does not suffer such requirement).

Related