1

I have an awk expression !a[$0]++ and I want to know the exact evaluation order. From the document of awk, ++ is at higher precedence than !. However, in this example, ++ is in its postfix form (not !++a[$0], which as I understand is basically tells awk, "evaluate other parts first, after that evaluate me". In this case, what's the evaluation order?

1

2 Answers 2

4

I suppose we can agree that a[$0] has the highest precedence here(*), so we can just simplify that as x.

I'm going to argue that what you read is correct, that ++ has higher precedence than the !, so these should be equal:

$ awk 'BEGIN{ x=123; tmp = !x++;  print tmp, x; }'
0 124
$ awk 'BEGIN{ x=123; tmp = !(x++);  print tmp, x; }'
0 124

And indeed they are. !x++ takes the value of x, returns it and increments afterwards. The returned value 123 is negated, yielding 0. The incremented value can later be found in x.

But let's try the other possibility too, that ! binds tighter:

$ awk 'BEGIN{ x=123; tmp = (!x)++;  print tmp, x; }'
awk: cmd. line:1: BEGIN{ x=123; tmp = (!x)++;  print tmp, x; }
awk: cmd. line:1:                           ^ syntax error

Well, whoops. Now, that can't work, since ! would first take the value of x, then negate it, returning 0. Now ++ is supposed to increment that, and store the result back. But 0 is just a value, not a variable, it cannot be stored to. Hence the error. (Something like (1+2)++ would give the same error.)

Therefore, ++ has a higher precedence, it just has a hidden side effect that is not apparent from the value it returns.

(* the subscription operator doesn't appear in the precedence table in the standard, as opposed to operator precedence tables of C, but if it had lower precedence, the expression really wouldn't work.)

2
  • 1
    I found the following two examples are more specific: awk 'BEGIN{ x=0; tmp = !++x; print tmp, x; }' and awk 'BEGIN{ x=0; tmp = !x++; print tmp, x; }'. Also I found that I can always consider !x++ as !x then x++ and !++x as ++x then !x. They always gives the correct result and is easier to understand. Jul 7, 2020 at 21:21
  • 1
    @Justalearner, yep, starting with x=0 makes the difference between pre- and post-increment apparent; and yes, it can help to mentally move them out of the expression. However, if you face something like a++ + ++a then that doesn't exactly work... (In C, that's not allowed, in awk it might be well-defined, I don't know. But just hope nobody does that in practice.)
    – ilkkachu
    Jul 7, 2020 at 21:32
2

A demo:

$ cat file
1 a
2 b
1 c
2 d
3 e
$ awk '!a[$1]++' file
1 a
2 b
3 e

++ happens first. Since it's a post-increment, for the first line, a[$1]++ increments the value of a[1] but returns zero, and the negation flips that to true, and the first line prints. Same with the 2nd line. For the third line, a[1] already has value 1, the post-increment sets the value to 2 and returns 1, which negated is false.


The mawk man page is more clear:

   New expressions are composed with the following operators in order of increasing
   precedence.

        assignment          =  +=  -=  *=  /=  %=  ^=
        conditional         ?  :
        logical or          ||
        logical and         &&
        array membership    in
        matching       ~   !~
        relational          <  >   <=  >=  ==  !=
        concatenation       (no explicit operator)
        add ops             +  -
        mul ops             *  /  %
        unary               +  -
        logical not         !
        exponentiation      ^
        inc and dec         ++ -- (both post and pre)
        field               $

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .