Is a semicolon prohibited after NAME in `for NAME do …`?

后端 未结 1 1614
我在风中等你
我在风中等你 2020-12-10 07:36

The bash manual lists the syntax for the for compound statement as

for name [ [ in [ word ... ]

相关标签:
1条回答
  • 2020-12-10 08:07

    Nicely spotted! I don't have a definite answer, but here is what the source code says about it:

    It's indeed not valid in the original Bourne shell from AT&T UNIX v7:

    (shell has just read `for name`):
           IF skipnl()==INSYM
           THEN chkword();
            t->forlst=item(0);
            IF wdval!=NL ANDF wdval!=';'
            THEN    synbad();
            FI
            chkpr(wdval); skipnl();
           FI
           chksym(DOSYM|BRSYM);
    

    Given this snippet, it does not appear to be a conscious design decision. It's just a side effect of the semicolon being handled as part of the in group, which is skipped entirely when there is no "in".

    Dash agrees that it's not valid in Bourne, but adds it as an extension:

            /*
             * Newline or semicolon here is optional (but note
             * that the original Bourne shell only allowed NL).
             */
    

    Ksh93 claims that it's valid, but says nothing of the context:

    /* 'for i;do cmd' is valid syntax */
    else if(tok==';')
        while((tok=sh_lex(lexp))==NL);
    

    Bash has no comment, but explicitly adds support for this case:

    for_command:    FOR WORD newline_list DO compound_list DONE
                {
                  $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
                  if (word_top > 0) word_top--;
                }
    ...
        |   FOR WORD ';' newline_list DO compound_list DONE
                {
                  $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
                  if (word_top > 0) word_top--;
                }
    

    In zsh, it is's just a side effect of the parser:

    while (tok == SEPER)
        zshlex();
    

    where (SEPER is ; or linefeed). Due to this, zsh happily accepts this loop:

    for foo; ; 
    ;
    ; ; ; ; ;
    ; do echo cow; done
    

    To me, this all points to an intentional omission in POSIX, and widely and intentionally supported as an extension.

    0 讨论(0)
提交回复
热议问题