NAME之后是否禁止使用分号?为NAME做...?

ric*_*ici 6 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
                 ;
Run Code Online (Sandbox Code Playgroud)

作为参考,linebreak是一个可能为空的序列,NEWLINEwhile sequential_sep是分号或a NEWLINE,可能后跟一个序列NEWLINE:

newline_list     :              NEWLINE
                 | newline_list NEWLINE
                 ;
linebreak        : newline_list
                 | /* empty */
                 ;
separator        : separator_op linebreak
                 | newline_list
                 ;
sequential_sep   : ';' linebreak
                 | newline_list
                 ;
Run Code Online (Sandbox Code Playgroud)

据我所知,这禁止语法for foo; do :; done.

在实践中,我尝试的所有shell(bash,dash,ksh和zsh)都接受for foo; do :; done并且for foo do :; done没有抱怨,无论Posix或他们自己的文档[注3].

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

附录

在XCU的描述中for loop,Posix似乎坚持使用换行符:

for循环的格式如下:

for name [ in [word ... ]]
do
compound-list
done

但是,在Rationale卷中,很明显语法是最后一个词:

显示的格式大量使用<newline>字符.请参阅XCU Shell语法中的语法,以准确描述<newline>和<分号>字符可以互换的位置.


笔记

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

  2. 该bash手册并不完全明确的关于新行; 它说的是:

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

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

  3. 两者ksh并且zsh似乎坚持认为在之后有分号或换行符name,尽管实现并不坚持.

    该ksh联机帮助页将语法列为:

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

    (我相信,在分号;do和;done代表"分号或换行".我找不到任何明确声明这一点,但它是使语法描述感的唯一方式.)

    该zsh手册所示:

    for name ... [ in word ... ] term do list done
        其中术语是至少一种换行符或; .

tha*_*guy 5

好找!我没有明确的答案,但这是源代码对此的说明:

它在AT&T UNIX v7的原始 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);
Run Code Online (Sandbox Code Playgroud)

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

Dash 同意它在 Bourne 中无效,但将其添加为扩展:

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

Ksh93声称它是有效的,但没有说明上下文:

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

Bash 没有评论,但明确添加了对这种情况的支持:

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--;
            }
Run Code Online (Sandbox Code Playgroud)

在 zsh 中,这只是解析器的一个副作用:

while (tok == SEPER)
    zshlex();
Run Code Online (Sandbox Code Playgroud)

其中(SEPER是;或换行)。因此,zsh 愉快地接受了这个循环:

for foo; ; 
;
; ; ; ; ;
; do echo cow; done
Run Code Online (Sandbox Code Playgroud)

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

  • 好的。两件小事:首先,我不认为您的链接是“原始”Bourne shell。[这个](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/cmd.c) 看来是“更多”原创,包括原版“使 C 成为一种不同的语言”宏。[Posix 基本原理](http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html) 也明确指出 System V shell(源自 Bourne shell)是基础:“System V shell被选为 POSIX.1-2008 的 Shell 和 Utilities 卷的起点。” (2认同)