为什么在sh循环中的“ do”之后没有“;”?


28

为什么在shell循环中单行写完;后没有字符do

这就是我的意思。多行编写时,for循环看起来像:

$ for i in $(jot 2)
> do
>     echo $i
> done

并在一行上:

$ for i in $(jot 2); do echo $i; done

该行,所有折叠行都在;其后得到a ,如果包含,则是错误的。某人可能比我聪明得多,这决定这样做是有原因的,这是正确的,但我不知道原因是什么。对我来说似乎不一致。do;

用相同的while循环了。

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
这是一致的:while/ 后面for都没有分号。
德米特里·格里戈里耶夫

立即想到的是,这是没有必要的。在其他地方,分号区分语句。
Paul Draper

因为那将是多余的。没有它就没有歧义。
user207421

Answers:


29

这就是命令的语法。看到复合命令

for name [ [in [words …] ] ; ] do commands; done

请特别注意: do commands

多数人将docommands放在单独的行中,以使其更易于阅读,但是没有必要,您可以编写:

for i in thing
do something
done

我知道这个问题专门针对shell,我已经链接到bash手册。它不是在shell手册中这样写的,而是在 Stephen Bourne为字节杂志撰写文章中以编写的

斯蒂芬说:

命令列表是由一个新行或分离或终止一个或多个简单命令的序列;(分号)。此外,保留等词语待办事项完成通常由一个新行之前或;...在各下面的命令列表的时间打开DO被执行。


5
为什么也不能像这样的分号也很有趣for i in thing; do; something; done。允许换行,但不能使用分号。
rexkogitans

@gidds:是的,完全正确。这也是shell语法的结构方式。该something拍摄对象和do适用于它。就像在英语句子结构中一样。
Peter Cordes

@gidds因为在条件中可以使用多个命令(在while语法中),所以必须使用文字(或其他语法),因此您需要一些区分条件命令和循环主体命令的内容。使用do将它们分开也许正是只是似乎最恰当不过了。
JoL

@rexkogitans实际上,我从未意识到这是语法错误。这个标题问题也很适合。这可能与不允许空着身体有关。但是,当用换行符创建一个空的主体时,您会得到稍微不同的语法错误,并且您的示例没有一个空的主体。
JoL

@rexkogitans此处的分号是循环语法的一部分,不同于其作为命令列表终止符的其他角色。换行符是不同的,因为如果没有其他要求或期望,则将换行符视为普通空格。Shell语法混乱。
chepner

12

我不知道这种语法的最初原因是什么,但是让我们考虑一下这样的事实:while循环可以在条件部分中使用多个命令,例如

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

在此,do关键字是区分条件部分和循环主体的必要条件。do是类似的关键字whileif并且then(和done)它似乎与其他一致,它后面不需要分号或换行符,而紧接在命令前。

替代方案(一般化为ifwhile)看起来有些难看,我们需要编写例如

if; [ -n "$a" ]; then
    some command here
fi

for循环的语法与此类似。


11

Shell语法基于前缀。它具有由特殊关键字引入的子句。某些条款必须结合在一起。

while环是做出来的一个或多个测试命令:

test ; test ; test ; ...

并通过一个或多个正文命令:

body ; body ; body ; ...

必须告诉外壳程序while循环开始。这就是这个while词的目的:

while test ; test ; test ; ...

但是随后,事情变得模棱两可。哪个命令是身体的起点?必须指出这一点,这就是do前缀的作用:

do body ; body ; body ; ...

最后,必须有一些迹象表明已经看到了最后的尸体;一个特殊的关键字可以 done做到这一点。

这些shell关键字即使在同一行上也不需要分号分隔。例如,如果您关闭多个嵌套循环,则可以只具有done done done ...

相反,... test ; body ... 如果它们位于同一行上,则分号位于中间。该分号被认为是终结符:它属于test。因此,如果在do关键字之间插入了关键字,则关键字必须在分号和之间body。如果它在分号的另一侧,则会错误地嵌入test命令的语法中,而不是放在命令之间。

shell语法最初由Stephen Bourne设计,并受到Algol的启发。伯恩(Bourne)非常喜欢Algol,以至于他在外壳程序源代码中使用了许多C宏,以使C看起来像Algol。您可以从Version 7 Unix浏览1979年的shell源代码。宏位于中mac.h,并且在各处使用它们。例如if报表呈现为IF... ELSE... ELIF... FI


源代码令人印象深刻。
keithpjolley,

是的,这是cpp的巡回演出。
stolenmoment,

7

我认为这do在这里并不特别。因为;无法启动命令列表,您会收到一个错误。如果是这样,则第一个命令将为空,并且语法不允许空命令(没有命令的变量赋值或重定向,是的,但是绝对没有,没有)。在任何地方,只要有命令列表,都是如此:

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

甚至:

$ ; ;
dash: 1: Syntax error: ";" unexpected

在所有这些地方,都应该有一个命令列表,因此前导;是意外的。


是的-我在'do'之后随意添加'\ n'的方式使我认为'do'是一个独立的令牌,而不是在后续块中进行的操作。
keithpjolley

它是如何,非转义换行符(否则似乎表现得就像在shell语法分号)后允许do(和if等)有关系吗?
Henning Makholm

@Henning在命令列表之前(和之后)允许任何数量的换行符。换行符和分号在其他方面的行为也有所不同。例如,别名扩展是在读取整行输入(即直到下一个换行符)时完成的,而不是按命令进行。
老师

3

语法为:

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

在命令列表中或者在“ do”或“ done”之前的任何换行符都可以用“;”代替。

在“ do”之后和“ in”之前允许使用换行符(但不能使用“;”),只是为了使事情看起来更好,但在那里要求换行符没有任何意义。


3

if 与它的关键字相似:当命令简短时,这是很容易理解的:

if   test_commands
then true_commands
else false_commands
fi

一旦我意识到我将所有东西都放在任意位置\ndo可以了(只要您不认为不能\n在其他命令和args之间插入任意位置)。
keithpjolley,

嗯,dothenelseARGS -如果他们成功了,你就不会被允许之前使用一个分号。它们是shell关键字(请参阅type if then elif else fi
参考资料

我想我的回答不够清楚。
keithpjolley,

我的观点是,“做”与任何“其他命令”都不一样。因此,它遵循不同的规则。
格琳·杰克曼(Glenn Jackman)

3

简短的答案,因为这是由POSIX shell语法指定的。在do和之间的任何内容done均视为一组语句,而;在顺序分隔符中:

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

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

此外,如果;有必要,该标准将明确指出,并包括sequential_sepafter Do


1
@drewbenn你的意思是第一个?那就是通过位置参数进行迭代。因此,如果您有一个名为./myscript.sh abc的脚本,其中abc是参数,则i的循环;做echo“ $ i”; 做会遍历ABC,但我仍然认为它需要分号,它的工作
谢尔盖Kolodyazhnyy

好了,我的疑虑也消除了
Sergiy Kolodyazhnyy

1

因为do是一个关键字,其后本质上是参数。Bash解释器了解更多。类似于没有“;” 在for命令中的i之后。您实际上可以在for中添加一个换行符,然后在其余的行中键入其余的行,这样就可以正常工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.