为什么我需要将“ do”与“ for”放在同一行?


28

1.总结

我不明白,为什么我需要E010打击规则


2.细节

我用bashate了掉毛.sh的文件。E010规则:

这样做不是在同一行

for 打击

  • 正确:

    #!/bin/bash
    for f in bash/*.sh; do
        sashacommand "$f"
    done
    
  • 错误:

    #!/bin/bash
    for f in bash/*.sh
        do sashacommand "$f"
    done
    

是否有任何有效的论据,为什么我需要fordo在同一行?


3.没用

我在以下位置找不到我的问题的答案:

  1. 谷歌
  2. 有关最佳编码实践的文章(示例
  3. bashate文档。我找到:

    一组有助于使控制块中的内容保持一致的规则。这些在连续的长行中被忽略,因为展开是一种“有趣的”


3
缩进do肯定有点不寻常,但是for ... \ndo\n<indent>body...非常普遍,我估计甚至比还要多for ... ; do。它也对此抱怨吗?
迈克尔·荷马'18

20
bashate样式检查器。风格本质上是主观的。如果您不喜欢它所施加的人工风格,请不要使用它。您应该考虑使用像shellcheck这样的语法/错误检查器。

@meuh,谢谢,我已经在使用ShellCheck。我的问题:E010-仅风格的规则或者在某些情况下,我需要做的不只是风格的原因在同一直线上。
СашаЧерных

10
从未有一个语法义务,有对在外壳的同一行。

1
@СашаЧерных不寻常的不是压痕本身,而是的压痕do。这就像缩进的左括号if,并在像C的语言又如同一行中添加代码,如果蟒蛇允许的话,将会把:一个if缩进在下一行并就同一行添加代码。这将使其与体内其他线条有所不同。
JoL

Answers:


60

从语法上讲,以下两个代码段是正确且等效的:

for f in bash/*.sh; do
    sashacommand "$f"
done
for f in bash/*.sh
    do sashacommand "$f"
done

后者可以可能说是难以阅读如do在循环体略微混淆的指令。如果循环包含多个命令,而下一个命令在新行上,则混淆将进一步突出显示:

for f in *
    do cmd1
    cmd2
done

...但是将其标记为“错误”是恕我直言,这是某人的个人观点,而不是客观真理。

通常,几乎所有内容;都可以用换行符代替。这两个;和换行符是命令终结。do是一个关键字,表示“此处(在此for循环中)遵循需要完成的操作”。

for f in *; do ...; done

是相同的

for f in *
do
   ...
done

并作为

for f in *; do
   ...
done

之所以要使用一个,是因为可读性和本地/个人风格约定。


个人想法:

非常长的循环标题中,我认为放入do新行可能是有意义的,例如

for i in animals people houses thoughts basketballs bees
do
    ...
done

要么

for i in        \
    animals     \
    people      \
    houses      \
    thoughts    \
    basketballs \
    bees
do
    ...
done

这同样适用于将thenif声明。

但是,这又取决于个人风格的偏好,或者团队/项目使用的任何编码风格。


您说“循环中的标题很长,可能需要在新的一行上添加”。您能提供一个原因吗?鉴于标头必须紧随其后do,我看不出将其放在下一行会提高可读性的原因。
康拉德·鲁道夫'18

3
@KonradRudolph我的终端宽80个字符。如果列表换行,则将其设置为一组连续的行(如上一个示例),如果该列表几乎换行,则将do(或then)单独放在一行上(如倒数第二个示例)。这一切都必须根据个人喜好而定。我不喜欢太长的行,部分是因为我的终端“仅”是80个字符宽,部分是因为我认为它看起来不整洁。我还发现很难分析很长的行以获取错误。
库萨兰达

1
似乎没有必要不断指出这是观点。bashate是一种样式指南,因此它本质上是基于观点。
巴马尔

4
@Barmar,请注意,这里的问题使用了诸如“需要”和“规则”之类的措词,而bashate自述文件则将do单独的一行称为“错误”。这些都是相当严格的短语,所以它不会似乎值得指出的是,恰恰相反,所谓的“规则”实际上只是一个主观的意见。
ilkkachu

2
@mtraceur是的,我不是在问个人喜好。但是请注意,您引用的可读性列限制不适用于代码。它仅适用于散文。眼动在代码读取方面根本不同,因此这些研究的证据不适用于此。至于使用历史原因不再适用的理由,那么,我们也使用了过去的8.3文件名。
康拉德·鲁道夫'18

28

请注意页面顶部的内容:

bashate:与bash脚本等效的pep8

PEP8是Python代码的样式指南。尽管Python人们似乎经常非常重视自己的样式问题[需要引用],但这仅仅是一个指南。我们既可以将放置do在与的同一行中for,又可以将其放置在自己的行中,从而带来一些好处。

这与{启动ifwhile块(或此类)时将C代码放入何处的讨论相同。


在文档下面的几行中,还有一条有趣的规则:

E020:函数声明格式不正确 ^function name {$

我认为这里的重点是该样式指南的作者认为使用function关键字对于读者识别函数声明是必需的。但是,function foo是一个非标准定义一个函数的方式:所述标准方法是foo()。两者之间的唯一区别(在Bash中)是,另一个较难移植到标准的Shell中,例如/bin/sh在Debian或Ubuntu中。

因此,我建议您将任何或所有此类样式指南都花一三盐,然后自己决定最佳样式。一个好的样式检查器可以禁用某些检查(bash必须bashate -i E010,E011忽略这两个检查。)一个优秀的样式检查器可以让您修改测试,例如,在此处检查E020的相反之处。


12

TL; DR:不需要。这只是一些家伙的意见。

像许多其他人for一样,几十年来我一直在写这样的循环:

for F in arg1 arg2 arg*
do
    somecommand "$F"
done

这种布局突出了do ... done包围循环主体的事实,就像C语言中的花括号一样,并且允许换行符分隔循环构造的组件。

当然,也有关于在哪里放置花括号的宗教战争,因此您可能会说陪审团仍未解决。恕我直言,这显然是它的设计工作方式;伯恩(Bourne)喜欢匹配的分隔符,请参见。if ... ficase ... esac以及较短版本中的分号表明您要使其比最初设计的要短。没有错; 科技的进步,许多曾经似乎是个好主意的事情现在都被皱眉了goto。但是,直到整个shell脚本编写社区采纳bashate作者的喜好之前,您无需做任何事情。


3
fi对应if,并且esac等来自68陵的唯一原因一个for循环的do没有结束的odod一个现有的标准实用程序的名称。参见en.wikipedia.org/wiki/ALGOL_68C#Algol_68C_and_Unix
库萨兰达

1
Right和用关键字分隔的块也被用在Algol系列的其他语言中,包括Pascal(使用begin ... end等)。
亚历克西斯

2
没有听到有关的故事od,这很有趣...(尽管,为什么不以结束循环rof,然后呢?)
亚历克西斯

2
@alexis好吧,就像Kusalananda所说的那样,它来自Algol 68,这是关键点。最初的Bourne shell的创建者Stephen Bourne深受该语言的影响,这就是他坚持使用该语法的原因。甚至当他在C.看到的是写的Bourne shell的源代码:minnie.tuhs.org//cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h 喔,方式,BEGINEND在那里也定义。
Sergiy Kolodyazhnyy

1
顺便说一下,整个格式样式也来自Algol 68。按照惯例,它FORDO也将位于单独的行上,除非整个循环很容易放在一行上。
reinierpost

3

令我惊讶的是,还没有人提到这种有效且可移植的语法:

set alfa bravo charlie
for q do
  echo "$q"
done

请注意,fordo在同一行,没有分号。结果:

alfa
bravo
charlie

我赞成这个答案,因为它提到了这种晦涩的语法(但在某些情况下对于干净的和可移植的外壳代码很关键),但是值得向人们明确解释这会掩盖位置参数,而且,我不得不提到唯一的“真正可移植(tm)”格式是for qdo位于单独的行上(此示例中的格式几十年前在原始Almquish shell(ash)上不受支持:in-ulm.de/~mascheck/various/ bourne_args /#endnote
mtraceur

虽然这个答案的例子对我的作品,将其应用到OP的问题的例子确实没有工作。
Scribblemacher

3

这里有几个很好的答案。总是带着一点点的风格指导,恕我直言和经验,对风格充斥着过多教条的任何社区都应该保持一点到中等的关注。几乎就像他们相信,当他们最终得到我加点的最后一个字母,并且最后一个T横越时,该软件便开始工作。有时,删除错误需要的不仅仅是完美的样式。

当我开始学习shell时,大多数脚本和教程都使用内嵌分号格式

for i in "$@"; do
    stuff
done

但是由于我没有经验,所以我很难找到它。和做-两者都是确认语法正确性所必需的。因此,我更喜欢单独执行下一行的操作。我不再这样做(双关语意)

此外,“ while”命令是一个不错的小技巧的绝佳选择:

while prep1; prep2; condition; do
    stuff
done

但是当prep命令有点笨重,和/或条件复杂时,最好将其格式化为

while
    prep1
    prep2
    condition
do
    stuff
done

然后将do附加为“; do”肯定是错误的。

您甚至可以变得更加紧张:

while
    if con1; then
      cond2
    elif cond3; then
      cond4
    elif cond5; then
      cond6
    else
      cond7
    fi
do
    stuff
done

因为每个语句都是一个表达式。注意这两种样式是如何被机会使用的。保持清洁,并且可读性强。以样式或“节省空间”的名义压缩或扭曲它,这会变成一团糟。

所以!考虑到外壳脚本的样式一般都是巴洛克式的,任何旨在提高清晰度并易于视觉访问重要符号的东西总是一件好事。

当您有一个小命令时,用命令内联编写“ do”的形式很不错-我看不到“ do”在视觉上是隐藏的,内部命令也没有真正被遮盖:

for i in whatever
  do stuff
done

我觉得这看起来很令人愉快,并且它与while,if和case类似:

while condition
  do stuff
end

if condition
  then stuff1
  else stuff2
fi

case $blah in
  a) stuff1;;
  b) stuff2;;
  c) stuff3;;
  *) stuffInfinity;;
esac

清晰和一般的整洁是Codd *所要求的。

*关系数据库理论之父Edgar F. Codd。


1
从来没有见过一个whileif之前的状态。凉!
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.