5

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) ?

3 Answers 3

14

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
8
  • cool stuff, good explanation :)
    – Ramesh
    Aug 8, 2014 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.
    – Hastur
    Aug 8, 2014 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, 2014 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, 2014 at 14:40
  • 1
    You should add i=0; before while(i<10){print i; ++i}, otherwise it's not equivalent and just UB.
    – simon
    Aug 8, 2014 at 19:04
6

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
3
  • 2
    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, 2014 at 19:26
  • @rici, it means your program wouldn't stop when it meets 0 in a line, right?
    – Zen
    Aug 9, 2014 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, 2014 at 3:00
3
  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.

3
  • 3
    $x is not equivalent to $x != "" because values like 0, +0, " 0", -0e12 also all evaluate to false. Aug 8, 2014 at 14:46
  • 1
    @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[1];if(a)print a" is true";else print a" is false"'} 0
    – rici
    Aug 9, 2014 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, 2014 at 7:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.