bashshellshlanguage-lawyerlanguage-history

Alternative for-loop construct


General comment: any new answer which gives a new and useful insight into this question will be rewarded with a bonus.


The Bash reference manual mentions that Bash supports the following for-loop constructs:

for name [ [in [words ...] ] ; ] do commands; done
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done

Surprisingly, the following for-loop constructs are also valid:

for i in 1 2 3; { echo $i; }
for ((i=1;i<=3;++i)); { echo $i; }

These unusual constructs are not documented at all. Neither the Bash manual, the Bash man-pages nor The Linux Documentation Project make any mention of these constructs.

When investigating the language grammar one can see that using open-and-close braces ({ commands; }) as an alternative to do commands; done is a valid construct that is implemented for both for-loops and select statements and dates back to Bash-1.14.7 [1].

The other two loop-constructs:

until test-commands; do consequent-commands; done
while test-commands; do consequent-commands; done

do not have this alternative form.


Since a lot of shell-languages are related, one can find that these constructs are also defined there and mildly documented. The KSH manual mentions:

For historical reasons, open and close braces may be used instead of do and done e.g.

for i; { echo $i; } 

while ZSH implements and documents similar alternatives for the other loop-constructs, but with limitations. It states:

For the if, while and until commands, in both these cases the test part of the loop must also be suitably delimited, such as by [[ ... ]] or (( ... )), else the end of the test will not be recognized.


Question: What is the origin of this construct and why is this not propagated to the other loop-constructs?


Update 1: There are some very useful and educational comments below this post pointing out that this is an undocumented Bourne Shell feature which seems to be the result of a C-vs-sh language battle in the early days.


Update 2: When asking the question: Why is this language feature not documented? to the Gnu Bash mailinglist, I received the following answer from Chet Ramey (current lead-developer of GNU bash):

It's never been documented. The reason bash supports it (undocumented) is because it was an undocumented Bourne shell feature that we implemented for compatibility. At the time, 30+ years ago, there were scripts that used it. I hope those scripts have gone into the dustbin of history, but who knows how many are using this construct now.

I'm going to leave it undocumented; people should not be using it anyway.


Related questions/answers:


Footnotes: [1] I did not find earlier versions, I do believe it predates this


Solution

  • [W]hy is this not propagated to the other loop-constructs?

    Braced forms of while and until commands would be syntactically ambiguous because you can't separate test-commands from consequent-commands without having a distinctive delimiter between them as they are both defined by POSIX to be compound lists.

    For example, a shell that supports such constructs can choose either one of the brace groups in the command below as consequent-commands and either way it would be a reasonable choice.

    while true; { false; }; { break; }
    

    Because of its ambiguous form, this command can be translated to either of the below; neither is a more accurate translation than the other, and they do completely different things.

    while true; do
        false
    done
    break
    
    while true; { false; }; do
        break
    done
    

    The for command is immune to this ambiguity because its first part—a variable name optionally followed by in and a list of words, or a special form of the (( compound command—can easily be distinguished from the brace group that forms its second part.

    Given that we already have a consistent syntax for while and until commands, I don't really see any point in propagating this alternate form to them.

    Wrt its origin, see: