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>和<分号>字符可以互换的位置.
该bash手册并不完全明确的关于新行; 它说的是:
在大多数情况下,命令描述中的列表可以通过一个或多个换行符与命令的其余部分分开,并且可以后跟换行符代替分号.
这清楚地表明前面的分号done可以用换行符替换,但似乎没有提到可以对前面的分号执行相同的转换do.
两者ksh并且zsh似乎坚持认为在之后有分号或换行符name,尽管实现并不坚持.
该ksh联机帮助页将语法列为:
for vname [ in word ... ] ;do list ;done
(我相信,在分号;do和;done代表"分号或换行".我找不到任何明确声明这一点,但它是使语法描述感的唯一方式.)
该zsh手册所示:
for name ... [ in word ... ] term do list done
其中术语是至少一种换行符或; .
好找!我没有明确的答案,但这是源代码对此的说明:
它在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)
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 中的一个故意遗漏,并且作为扩展得到了广泛和有意的支持。