为什么要避免在 bash 脚本中使用“&&”?

Isa*_*ari 47 shell bash

我正在编写一个 bash 脚本,其中包含一个简单的 if 部分,其中有两个条件:

  if [[ -n $VAR_A ]] && [[ -n $VAR_B ]]; then
    echo >&2 "error: cannot use MODE B in MODE A" && exit 1
  fi
Run Code Online (Sandbox Code Playgroud)

一位高级工程师审查了我的代码并评论道:

当您可以简单地执行后续行中的两个命令时,请避免使用 && 。

他没有进一步解释。但出于好奇,我想知道这是否属实,以及避免使用&&.

cho*_*oba 94

审稿意见大概是指&&操作符的第二种用法。我想,如果失败,您不想不退出echo,因此将命令写在单独的行上更有意义:

if [[ -n $VAR_A ]] && [[ -n $VAR_B ]]; then
    echo >&2 "error: cannot use MODE B in MODE A"
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

顺便说一句,在 bash 中你可以包含&&以下[[ ... ]]条件:

if [[ -n $VAR_A && -n $VAR_B ]]; then
Run Code Online (Sandbox Code Playgroud)

  • @IsaIkari,另请参阅 `[ -n "$VAR_A" ] && [ -n "$VAR_B" ]`,这是标准的 `sh` 语法(然后您可以删除对 `bash` 的依赖)。 (26认同)
  • 我发现它更具可读性。它也快一点。 (11认同)
  • @G.Sliepen,绝对不应该使用“-a”和“-o”二进制“[”运算符。它们会导致不可靠的测试表达式,因此已被弃用。 (5认同)

ter*_*don 69

这不是一般性的反对评论&&。我怀疑与您交谈的工程师正在考虑(可能性很小,但理论上仍然可能)本身echo失败的情况。如果你有这个:

if error; then
    echo foo && exit
fi
Run Code Online (Sandbox Code Playgroud)

然后,如果由于某种原因echo命令失败,则该命令exit将不会运行,因此您实际上不会捕获错误。如果将它们分开,exit即使echo失败也会运行:

if error; then
    echo foo
    exit 
fi
Run Code Online (Sandbox Code Playgroud)

所以工程师确实是对的,将这两个具体的语句分开比较安全。但是,不要将其解释为反对使用 的一般指令&&。您应该简单地确保&&仅在您确实希望使一个命令的执行取决于另一个命令的成功执行的情况下使用。

例如,这完全没问题:

command && echo 'command worked!' 
Run Code Online (Sandbox Code Playgroud)

  • 另外@IsaIkari,如果您想独立执行两个命令,但**真的希望它们在一行内**(出于美观原因,节省屏幕空间或其他原因),您可以使用“;”而不是“&&” - 例如。`回声富;出口1`。这与两行变体的工作方式相同 (31认同)
  • 如果输出流关闭,“echo”将会失败。我最近看到一些用户发布的代码执行“2>&-”而不是“2>/dev/null”,如果对“echo”使用的流执行此操作,则该实用程序将失败并“退出” ` 不会被调用。 (6认同)

小智 23

人们已经为失败提出了很好的理由echo。除了关于正确性的这一点之外,您还可以说明可读性。你写了

echo >&2 "error: cannot use MODE B in MODE A" && exit 1
Run Code Online (Sandbox Code Playgroud)

当我在脑子里把它翻译成英语时,我得到的是这样的信息:

将以下内容打印到标准错误:“错误:无法在模式 A 中使用模式 B”
如果打印操作成功完成,则以代码 1 退出

读到这里的人可能会问,“为什么你要费心指定打印操作是否成功完成”?在本例中,这两行比 更简单,&&因为代码中的逻辑较少。推荐的两行替代方案的英文版本是:

将以下内容打印到标准错误:“错误:无法在模式 A 中使用模式 B”
,然后以代码 1 退出

这不那么复杂,所以阅读它需要更少的脑力。我们不会想知道它&&的用途。

  • 这对我来说是重点。为什么只有当“echo”成功时才决定退出,这是没有意义的。相反,如果“echo”失败,这就是想要尽快中止的又一个原因。你甚至可以这样写: `echo>&2 error || 出口; exit 1`,即如果`echo`失败则以`echo`的失败退出状态退出,否则以代码1退出。 (4认同)
  • @StéphaneChazelas *如果 `echo` 失败,则以 `echo` 的失败退出状态退出* IMO 这不是一个好主意。如果此脚本中的退出代码有任何意义,则将其替换为“echo”中的退出代码将打破这一点。如果脚本中的退出代码没有任何意义,而是在将来添加该含义,则仅当“echo”失败时才以“echo”的失败退出状态退出,这会打开一扇很难找到的大门,只是-关于不可能重新创建错误。如果将含义添加到脚本退出状态的值中,则很可能是“echo ... ||” 退出;`将会被错过。 (3认同)

Aus*_*arn 21

简而言之,您不应假设在退出路径中执行的代码本身不会导致错误。这并非特定于 shell 脚本,而是一般意义上的良好编程建议。

\n

通过使用echo \xe2\x80\x98foo\xe2\x80\x99 && exit,您只会在echo \'foo\'成功时退出。失败的可能性确实非常低,但你不应该指望这一点,特别是当正确执行任务的成本如此之低时。当您有一个更复杂的退出路径可能会调用代码中的其他函数并且 \xe2\x80\x99 并不明显您的退出路径只会调用 \xe2\x80\x98mostly safe\xe2\x80\ 时,这一点变得更加重要x99之类的东西echo,但它\xe2\x80\x99s通常是在所有情况下使用\xe2\x80\x98正确\xe2\x80\x99代码的良好形式,这样你就养成了正确执行它的习惯(这样你的同事就知道什么期待)。

\n

如果出于某种原因,您需要将其作为一行(但我认为永远不应该出现这种情况),则正确的语法是echo \'foo\' ; exit,这会导致解析器将这两个命令视为位于不同的行上。

\n
\n

顺便说一句,在你的条件中,你几乎总是应该选择:

\n
if [[ -n $VAR_A && -n $VAR_B ]]; then\n
Run Code Online (Sandbox Code Playgroud)\n

这是 bash 特定的,但比您当前拥有的更有效,或者:

\n
if [ -n "$VAR_A" ] && [ -n "$VAR_B" ]; then\n
Run Code Online (Sandbox Code Playgroud)\n

它将在任何符合 POSIX 的 shell 中以语义等效的方式工作。

\n

  • `[[ -n $VAR_A && -n $VAR_B ]]` 不是 `bash` 特定的,它来自 ksh,除了 `bash` 之外,还受 `zsh` 和 `yash` 支持(但无效) `sh` 语法) (2认同)