How to understand the command awk '{for(x=1;\$x;++x)print \$x}'?

I know this command

awk '{for(x=1;\$x;++x)print \$x}'

will print out all columns in a line.

wouldn't this ++x change x to 2, and thus print \$2 first? As I understood based on this: https://stackoverflow.com/questions/1812990/incrementing-in-c-when-to-use-x-or-x

And what does the \$x do in for(x=1;\$x;++x) ?

No. The for(i=0;i<10;i++) is a classic programming construct (see Traditional for loops) that is present in many languages. It can be broken down to:

start-expression; end-condition; end-of-iteration-expression

In other words, what I wrote above means "initialize i to 0 and, while i is less than 10, do something and then increment i by 1. Yes the syntax is confusing but that's just the way it is. The end-of-iteration-expression (++x in this case) is executed once at the end of each loop. It is equivalent to writing:

while(i<10){print i; ++i}

As for the \$x, I believe that just checks that a field of that number exists and that its contents do not evaluate to false (as explained in Mathias's answer below). \$N will return true if the field number N exists and is not a type of false. For example:

\$ echo "a b c d" | awk '(\$4){print "yes"}'
yes
\$ echo "a b c d" | awk '(\$14){print "yes"}' ## prints nothing, no \$14
\$ echo "a b c 0" | awk '(\$4){print "yes"}' ## prints nothing, \$4 is 0

As you can see above, the first command prints yes because there is a \$4. Since there is no \$14, the second prints nothing. So, to get back to your original example:

awk '{for(x=1;\$x;x++)print \$x}'
___ __ ___
|   |  |
|   |  |-----> increment x by 1 at the end of each loop.
|   |--------> run the loop as long as there is a field number x
|------------> initialize x to 1
• cool stuff, good explanation :) Aug 8 '14 at 14:22
• But why if I write ` echo 'a b c d e' | awk '{for(x=1;\$5;++x)print NR,\$x}'` I have an infinite loop? With echo 'a b c d ' | awk '{for(x=1;\$5;++x)print NR,\$x}' it prints nothing as expected. Aug 8 '14 at 14:25
• @Hastur because there will always be a \$5. So, every time the loop runs, it checks whether \$5 exists. Because it does, the loop will be executed. This will go on until there is no \$5 which will never happen because your input has at least 5 fields. The second prints nothing because the condition fails immediately: there is no \$5.
– terdon
Aug 8 '14 at 14:31
• The first part is not a condition. It's more like start-expression; end-condition; end-of-iteration-expression (note that the end-of-iteration-expression doesn't have to be an increment). Aug 8 '14 at 14:40
• You should add i=0; before while(i<10){print i; ++i}, otherwise it's not equivalent and just UB. Aug 8 '14 at 19:04

Since terdon provided a comprehensive answer I just want to add that if any column evaluates to false, the for statement ends the loop, as you can see in this example:

\$ echo 1 2 3 4 5 0 6|awk '{for(x=1;\$x;++x)print \$x}'
1
2
3
4
5
• A more correct awk program would be {for(x=1;\$x"";++x)print \$x}. This works because it fixes the type of the test expression as a string, rather than a numeric string, and a string is false only if it is empty. With the default FS, an empty field is possible only at the end of the fields.
– rici
Aug 8 '14 at 19:26
• @rici, it means your program wouldn't stop when it meets 0 in a line, right?
– Zen
Aug 9 '14 at 2:56
• @rici, I've tested it, it avoid the 0 problem. It's quite important in this issue, I think you really should write a supplement answer for this point. Great point.
– Zen
Aug 9 '14 at 3:00
1. ++x and x++ are functionally equivalent when used stand-alone.  As discussed in the Stack Overflow question that you referenced,

• There may be performance (i.e., timing) differences.
• The results of something = ++x; and something = x++; are different – but your example isn’t doing that.

So, as far as the incrementing of x is concerned, your example is equivalent to

awk '{for(x=1;\$x;x++) print \$x}'

2. The standalone \$x is equivalent to \$x != "", so the loop will iterate until it encounters a blank field.  This is a lazy shortcut for x <= NF, where NF is the number of fields in the current record (line).  For the purposes of your example, this is harmless, AFAICT.  But, if you specify a non-default field separator,

awk -F, '{for(x=1;\$x;++x) print \$x}'

this will try to do the same as your example, but splitting lines at commas.  If you type a b, it will output a b.  If you type a,b, it will print a and b on separate lines.  But if you input a,,b, it will output a and then stop, because \$2 is null.

• \$x is not equivalent to \$x != "" because values like 0, +0, " 0", -0e12 also all evaluate to false. Aug 8 '14 at 14:46
• @StéphaneChazelas: It's more complicated than that. "0" only evaluates to false if it's considered a numeric string. awk 'BEGIN {a="0";if(a)print a" is true";else print a" is false"'}. Contrast the numeric string case: awk -v a=0 'BEGIN {if(a)print a" is true";else print a" is false"'} or awk 'BEGIN {a=ARGV;if(a)print a" is true";else print a" is false"'} 0
– rici
Aug 9 '14 at 5:32
• @StéphaneChazelas (of course, in the context of G-Man's incorrect assertion, your statement is correct because a field is a numeric string context.)
– rici
Aug 9 '14 at 7:18