问题描述
bash手册将for
复合语句的语法列出为
The bash manual lists the syntax for the for
compound statement as
for name [ [ in [ word ... ] ] ; ] do list ; done
for name [ [ in [ word ... ] ] ; ] do list ; done
表示如果省略 in
子句,则 do
前的分号是可选的. [笔记2].
which implies that the semicolon before do
is optional if the in
clause is omitted. [Note 2].
但是,Posix规范仅列出了for_clause
的以下三种产品:
However, the Posix specification lists only the following three productions for 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
的序列:
For reference, linebreak
is a possibly-empty sequence of NEWLINE
while sequential_sep
is either a semicolon or a NEWLINE
, possibly followed by a sequence of 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
.
As far as I can see, that prohibits the syntax for foo; do :; done
.
实际上,我尝试过的所有shell(bash,dash,ksh和zsh)都可以接受for foo; do :; done
和for foo do :; done
,而无需抱怨,无论Posix或自己的文档如何[注3].
In practice, all the shells I tried (bash, dash, ksh and zsh) accept both for foo; do :; done
and for foo do :; done
without complaint, regardless of Posix or their own documentation [Note 3].
这是Posix标准在语法上的偶然遗漏吗?还是应该在该语法中使用分号被视为对标准的(通常实现的)扩展?
Is this an accidental omission in the grammar in the Posix standard, or should the use of the semicolon in that syntax be considered a (commonly-implemented) extension to the standard?
在for loop
的XCU描述中,Posix似乎坚持换行:
In the XCU description of the for loop
, Posix seems to insist on newlines:
for name [ in [word ... ]]
do
compound-list
done
for name [ in [word ... ]]
do
compound-list
done
但是,在基本原理"卷中清楚地表明,语法旨在成为硬道理:
However, in the Rationale volume, it is made clear that the grammar is intended to be the last word:
注释
-
显然,这是第一个将的问题配对的SO问题. shell 和 language-lawyer 一个>.没有 idle-curiosity 的问题,可能更合适.
Apparently this is the first SO question which pairs shell and language-lawyer. There is no idle-curiosity, which might have been more appropriate.
bash
手册对换行符并不十分明确;它说的是:
The bash
manual is not entirely explicit about newlines; what it says is:
很明显,可以用换行符替换 done
之前的分号,但是似乎没有提到可以对 .
That makes it clear that the semicolon preceding done
can be replaced by a newline, but does not seem to mention that the same transformation can be performed on the semicolon preceding do
.
ksh
和zsh
似乎都坚持在name
之后使用分号或换行符,尽管实现方式并不坚持使用分号或换行符.
Both ksh
and zsh
seem to insist that there be either a semicolon or a newline after the name
, although the implementations don't insist on it.
ksh
联机帮助页列出了以下语法:
The ksh
manpage lists the syntax as:
(我相信;do
和;done
中的分号代表分号或换行符".我无法找到任何明确的声明来表示这种效果,但这是理解语法描述的唯一方法)
(I believe that the semicolon in ;do
and ;done
represents "a semicolon or a newline". I can't find any definite statement to that effect but it is the only way to make sense of the syntax description.)
zsh
手册显示:
推荐答案
很漂亮!我没有确切的答案,但这是源代码对此的解释:
Nicely spotted! I don't have a definite answer, but here is what the source code says about it:
在来自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);
鉴于此摘要,这似乎不是一个有意识的设计决定.这只是将分号作为in
组的一部分来处理的副作用,当没有"in"时,会完全跳过分号.
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同意无效在伯恩(Bourne)中,但将其添加为扩展名:
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 声称它是有效的,但未提及上下文:
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没有评论,但明确添加了对这种情况:
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--;
}
在zsh中,它只是的副作用解析器:
while (tok == SEPER)
zshlex();
其中(SEPER
是 ;
或换行).因此,zsh很高兴接受此循环:
where (SEPER
is ;
or linefeed). Due to this, zsh happily accepts this loop:
for foo; ;
;
; ; ; ; ;
; do echo cow; done
对我来说,所有这些都表明POSIX中的故意遗漏,并且作为扩展被广泛而有意地支持.
To me, this all points to an intentional omission in POSIX, and widely and intentionally supported as an extension.
这篇关于是否禁止在"for NAME"中使用NAME后的分号...?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!