Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: while loop grammar question



To be clear, not  even the last command in a condition need be a test _expression_; `while a; bunch; of; commands; are; true: do` is a perfectly cromulent infinite loop. :) more practically, something like `while ! tail -n 1 $logfile | grep -q done` is a potentially finite loop with no bracketed test.

Mark J. Reed <markjreed@xxxxxxxxx>


On Sun, Dec 8, 2024 at 16:00 Lawrence Velázquez <larryv@xxxxxxx> wrote:
On Sun, Dec 8, 2024, at 9:25 AM, Ray Andrews wrote:
> Yikes, gotchas within gotchas.  I expect something so fundamental is
> pretty deeply coded so might have to be lived with.

I would be surprised if there were many users reliant on this
optional-loop-body behavior.


> Even given your
> explanations above, it seems strange that the 'while' loop loops -- it
> knows it's a loop but it doesn't understand it's own break condition.

It does understand its own break condition.  The issue is that the
break condition -- e.g., the command whose exit status matters --
is not where you think it is.  In a nutshell, this:

        while ((0)) {
                print foo
        }
        # ...

is equivalent to:

        while
                ((0))
        do
                print foo
        done
        # ...

but this:

        while ((0))
        {
                print foo
        }
        # ...

effectively works like:

        while
                ((0))
                {
                        print foo
                }
                # ...
        do
                :
        done

(I'm using "# ..." to represent any subsequent commands.)

Remember that the test is a list of zero or more commands.  So the
last command of the script, whatever it is, controls the loop.


> So it is critical where the left brace is?

Yes.  If the left brace is on its own line, then it begins a { ... }
grouping command that is part of the test.  Consider that this is
valid (if silly):

        x=0
        while ((++x))
        {
                print testing
        }
        [[ x -le 3 ]]
        do
                print $x
        done

On the other hand, this is not valid:

        ((++x)) {
                print testing
        }

which is why it could be repurposed for the short commands.


> As usual I'm thinking in C here.

An understandable impulse but one that does not serve you well when
thinking about shell scripting grammar.


> So what would be the hygienic form?  Stick with 'do/done' all the
> time?

Yes.  It's predictable and largely portable (for those who care
about that, like I do).


> I'd prefer it throw an error consistently rather than accept the
> 'for' form above, which I use all the time. At least I'd expect 'for'
> and 'while' to have exactly the same grammar.

The difference is that "while" is followed by a list of arbitrary
commands that can be separated by LFs (for the short form, only the
final one has to be something like ((...)) or [[...]]).  With an
arithmetic "for" loop, the ((...)) construct is actually part of
the syntax, so there's no confusion about where the loop body begins.

--
vq



Messages sorted by: Reverse Date, Date, Thread, Author