`for NAME do ...` 中的 NAME 后是否禁止分号?
Is a semicolon prohibited after NAME in `for NAME do ...`?
bash 手册将 for
复合语句的语法列为
<strong>for</strong>名称[[<strong>in</strong>[<em>word</em><em>...</em> ] ] <strong>;</strong> ] <strong>do</strong> <em>list</em> <strong>;</strong> <strong>完成</strong>
这意味着如果省略 in
子句,则 do
之前的分号是可选的。 [注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
;
供参考,linebreak
是 NEWLINE
的可能空序列,而 sequential_sep
是分号或 NEWLINE
,可能后跟 NEWLINE
:
newline_list : NEWLINE
| newline_list NEWLINE
;
linebreak : newline_list
| /* empty */
;
separator : separator_op linebreak
| newline_list
;
sequential_sep : ';' linebreak
| newline_list
;
据我所知,这禁止语法 for foo; do :; done
.
实际上,我尝试过的所有 shell(bash、dash、ksh 和 zsh)都毫无怨言地同时接受 for foo; do :; done
和 for foo do :; done
,无论 Posix 或他们自己的文档 [注 3].
这是 Posix 标准语法中的意外遗漏,还是应该将在该语法中使用分号视为标准的(通常实现的)扩展?
附录
在for loop
的XCU描述中,Posix似乎坚持换行:
The format for the for loop is as follows:
for <em>name</em> <strong>[</strong> in <b>[</b><i>word</i> ... <strong>]]</strong><br />
do<br />
<em>compound-list</em><br />
done
但是在Rationale卷中,明确指出文法是为了硬道理:
The format is shown with generous usage of <newline> characters. See the grammar in XCU Shell Grammar for a precise description of where <newline> and <semicolon> characters can be interchanged.
注释
显然这是第一个配对 shell and language-lawyer. There is no idle-curiosity 的 SO 问题,这可能更合适。
bash
手册没有完全明确地说明换行符;它说的是:
In most cases a list in a command's description may be separated from the rest of the command by one or more newlines, and may be followed by a newline in place of a semicolon.
说的很清楚done
前面的分号可以用换行符代替,但是好像没有说可以对done
do
.
前的分号
ksh
和zsh
似乎都坚持在<i>name[=117之后要有一个分号或换行符=]
,尽管实现并不坚持。
ksh
联机帮助页列出的语法为:
<b>for</b> <i>vname</i> [ <b>in</b> <i>word</i> ... ] <b>;do</b> <i>list</i> <b>;done</b>
(我相信 <b>;do</b>
和 <b>;done[=111 中的分号=]
表示“分号或换行符”。我找不到任何明确的说明,但这是理解语法描述的唯一方法。)
zsh
手册显示:
<b>for</b> <i>name</i> ... [ <b>in</b> <i>word</i> ... ] <i>term</i> <b>do</b> <i>list</i> <b>done</b>
where term is at least one newline or ;.
发现不错!我没有明确的答案,但源代码是这样说的:
在来自 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 (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , word_lineno[word_top]);
if (word_top > 0) word_top--;
}
...
| FOR WORD ';' newline_list DO compound_list DONE
{
$$ = make_for_command (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , 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 中的一个故意遗漏,并作为扩展得到广泛和有意的支持。
bash 手册将 for
复合语句的语法列为
<strong>for</strong>名称[[<strong>in</strong>[<em>word</em><em>...</em> ] ] <strong>;</strong> ] <strong>do</strong> <em>list</em> <strong>;</strong> <strong>完成</strong>
这意味着如果省略 in
子句,则 do
之前的分号是可选的。 [注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
;
供参考,linebreak
是 NEWLINE
的可能空序列,而 sequential_sep
是分号或 NEWLINE
,可能后跟 NEWLINE
:
newline_list : NEWLINE
| newline_list NEWLINE
;
linebreak : newline_list
| /* empty */
;
separator : separator_op linebreak
| newline_list
;
sequential_sep : ';' linebreak
| newline_list
;
据我所知,这禁止语法 for foo; do :; done
.
实际上,我尝试过的所有 shell(bash、dash、ksh 和 zsh)都毫无怨言地同时接受 for foo; do :; done
和 for foo do :; done
,无论 Posix 或他们自己的文档 [注 3].
这是 Posix 标准语法中的意外遗漏,还是应该将在该语法中使用分号视为标准的(通常实现的)扩展?
附录
在for loop
的XCU描述中,Posix似乎坚持换行:
The format for the for loop is as follows:
for <em>name</em> <strong>[</strong> in <b>[</b><i>word</i> ... <strong>]]</strong><br /> do<br /> <em>compound-list</em><br /> done
但是在Rationale卷中,明确指出文法是为了硬道理:
The format is shown with generous usage of <newline> characters. See the grammar in XCU Shell Grammar for a precise description of where <newline> and <semicolon> characters can be interchanged.
注释
显然这是第一个配对 shell and language-lawyer. There is no idle-curiosity 的 SO 问题,这可能更合适。
bash
手册没有完全明确地说明换行符;它说的是:In most cases a list in a command's description may be separated from the rest of the command by one or more newlines, and may be followed by a newline in place of a semicolon.
说的很清楚
前的分号done
前面的分号可以用换行符代替,但是好像没有说可以对done
do
.ksh
和zsh
似乎都坚持在<i>name[=117之后要有一个分号或换行符=]
,尽管实现并不坚持。ksh
联机帮助页列出的语法为:<b>for</b> <i>vname</i> [ <b>in</b> <i>word</i> ... ] <b>;do</b> <i>list</i> <b>;done</b>
(我相信
<b>;do</b>
和<b>;done[=111 中的分号=]
表示“分号或换行符”。我找不到任何明确的说明,但这是理解语法描述的唯一方法。)zsh
手册显示:<b>for</b> <i>name</i> ... [ <b>in</b> <i>word</i> ... ] <i>term</i> <b>do</b> <i>list</i> <b>done</b>
where term is at least one newline or ;.
发现不错!我没有明确的答案,但源代码是这样说的:
在来自 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 (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , word_lineno[word_top]);
if (word_top > 0) word_top--;
}
...
| FOR WORD ';' newline_list DO compound_list DONE
{
$$ = make_for_command (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , 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 中的一个故意遗漏,并作为扩展得到广泛和有意的支持。