在NAME之后,对于NAME,是否禁止使用分号...?

时间:2016-04-27 17:27:25

标签: shell posix language-lawyer

bash手册将for复合语句的语法列为

for name [ [ in [ word ... ] ] ; ] do list ; done

这意味着如果省略 do 子句, in 之前的分号是可选的。 [笔记2]。

但是,Posix规范仅列出for_clause的以下三个产品:

for_clause       : For name linebreak                            do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group
                 ;

作为参考,linebreakNEWLINE可能为空的序列,而sequential_sep是分号或NEWLINE,可能后跟{{1}的序列}}:

NEWLINE

据我所知,这禁止语法newline_list : NEWLINE | newline_list NEWLINE ; linebreak : newline_list | /* empty */ ; separator : separator_op linebreak | newline_list ; sequential_sep : ';' linebreak | newline_list ;

在实践中,我尝试过的所有shell(bash,dash,ksh和zsh)都会接受for foo; do :; donefor foo; do :; done而无需投诉,无论Posix或他们自己的文档[注3]。

这是Posix标准中语法中的意外遗漏,还是在该语法中使用分号是否应被视为标准的(通常实施的)扩展?

附录

for foo do :; done的XCU描述中,Posix似乎坚持使用换行符:

  

for 循环的格式如下:

     

for loop

然而,在基本原理卷中,很明显语法是最后一个词:

  

显示的格式大量使用< newline>字符。有关< newline>的详细说明,请参阅XCU Shell语法中的语法。和<分号>字符可以互换。

注释

  1. 显然,这是第一个将配对的SO问题。没有,这可能更合适。

  2. for name [ in [word ... ]]
    do
    compound-list
    done
    手册并未完全明确换行;它的意思是:

      

    在大多数情况下,命令描述中的 list 可以通过一个或多个换行符与命令的其余部分分开,并且可以后跟换行符代替分号。

    这清楚地表明 bash 之前的分号可以用换行符替换,但似乎没有提到可以对前面的分号执行相同的转换 done

  3. doksh似乎都坚持在zsh之后有分号或换行符,尽管这些实现并不坚持。

    name联机帮助页将语法列为:

      

    ksh

    (我相信for vname [ in word ... ] ;do list ;done;do中的分号代表“分号或换行符”。我找不到任何明确的声明,但这是唯一有意义的方法语法描述。)

    ;done手册显示:

      

    zsh
      其中 term 至少是一个换行符或;

1 个答案:

答案 0 :(得分:4)

很好地发现了!我没有明确的答案,但这是源代码所说的内容:

在AT& T UNIX v7的the original Bourne shell中确实无效:

(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);

鉴于此片段,它似乎不是一个有意识的设计决定。这只是作为in组的一部分处理的分号的副作用,当没有“in”时,它会被完全跳过。

Dash同意它是not valid in Bourne,但是将其添加为扩展程序:

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

Ksh93 claims that it's valid,但没有说明背景:

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

Bash没有评论,但是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--;
            }

在zsh中,它只是一个side effect of the parser

while (tok == SEPER)
    zshlex();

其中(SEPER; or linefeed)。因此,zsh很乐意接受这个循环:

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

对我来说,这一切都指向POSIX中的故意遗漏,并作为扩展广泛和有意地支持。