为什么设置-e; true && false && true不退出?

Mar*_*tus 10 bash

根据这个接受的答案使用set -e内置应该足以让bash脚本退出第一个错误.然而,以下脚本:

#!/usr/bin/env bash

set -e

echo "a"
echo "b"
echo "about to fail" && /bin/false && echo "foo"
echo "c"
echo "d"
Run Code Online (Sandbox Code Playgroud)

打印:

$ ./foo.sh 
a
b
about to fail
c
d
Run Code Online (Sandbox Code Playgroud)

删除echo "foo" 确实会停止脚本; 但为什么?

Eta*_*ner 5

因为答案不够具体.

应该说(粗体文字是我的补充):

#任何失败的后续简单命令都会导致shell脚本立即退出

由于手册页如此读取:

-e      Exit  immediately if a simple command (see SHELL GRAMMAR
        above) exits with a non-zero status.  The shell does not
        exit  if  the  command that fails is part of the command
        list immediately following a  while  or  until  keyword,
        part  of the test in an if statement, part of a && or ??
        list, or if the command’s return value is being inverted
        via  !.   A  trap on ERR, if set, is executed before the
        shell exits.
Run Code Online (Sandbox Code Playgroud)

SHELL GRAMMAR因此扩展:

SHELL GRAMMAR
   Simple Commands
       A simple command is a sequence of optional  variable  assignments  fol-
       lowed  by  blank-separated  words and redirections, and terminated by a
       control operator.  The first word specifies the command to be executed,
       and  is  passed  as  argument  zero.  The remaining words are passed as
       arguments to the invoked command.

       The return value of a simple command is its exit status,  or  128+n  if
       the command is terminated by signal n.
Run Code Online (Sandbox Code Playgroud)

  • `bash` 4.3的手册页最后澄清了这一点:"[...]在&&或||列表**中执行的任何命令的一部分**除了最后的&&或||**[...]之后的命令 (3认同)

Joh*_*024 5

为了简化EtanReisner的详细答案,set -e仅在出现“未捕获”错误时退出。在您的情况下:

echo "about to fail" && /bin/false && echo "foo"
Run Code Online (Sandbox Code Playgroud)

失败的代码,/bin/false之后&&将测试其退出代码。由于&&测试了退出代码,因此可以假设程序员知道自己在做什么,并预计该命令可能会失败。如此,脚本不会退出。

相比之下,请考虑:

echo "about to fail" && /bin/false
Run Code Online (Sandbox Code Playgroud)

程序不会测试或退出的退出代码/bin/false。因此,当/bin/false失败时,set -e将导致脚本退出。

/bin/false失败时退出的替代方法

考虑:

set -e
echo "about to fail" && /bin/false ; echo "foo"
Run Code Online (Sandbox Code Playgroud)

如果/bin/false失败,此版本将退出。因此,与使用情况一样&&,最终语句echo "foo"只有在/bin/false成功的情况下才会执行。


fin*_*oot 5

我遇到过set -eBash 脚本,但无法理解最后一个&&||a&&||列表中的命令的评估会发生什么情况。我知道以下来自http://man7.org/linux/man-pages/man1/bash.1.html的 引用set -e

&&如果失败的命令是 (...)或列表中执行的任何命令的一部分(最后一个或(...)||后面的命令除外) ,则 shell 不会退出&&||

为了测试这一点,我编写了一个小 Bash 脚本:

#!/bin/bash

bash -c "set -e ; true           ; echo -n A"
bash -c "set -e ; false          ; echo -n B"
bash -c "set -e ; true  && true  ; echo -n C"
bash -c "set -e ; true  && false ; echo -n D"
bash -c "set -e ; false && true  ; echo -n E"
bash -c "set -e ; false && false ; echo -n F"
bash -c "set -e ; true  || true  ; echo -n G"
bash -c "set -e ; true  || false ; echo -n H"
bash -c "set -e ; false || true  ; echo -n I"
bash -c "set -e ; false || false ; echo -n J"

echo ""
Run Code Online (Sandbox Code Playgroud)

它打印:

ACEFGHI
Run Code Online (Sandbox Code Playgroud)

关于:

true没有非零状态。因此,shell 不会退出。

关于乙:

false确实具有非零状态并且不是 a&&||列表的一部分。因此,shell 退出。

关于C:

这是一个&&||列表。我们必须查看最后一个&&或后面的命令||。该命令true不具有非零状态。因此,命令是否被执行并不重要 - shell 不会以任何方式退出。

关于D:

这是一个&&||列表。我们必须查看最后一个&&或后面的命令||。这次,该命令false确实具有非零状态。因此,我们必须检查是否false正在评估它 - 确实是这样,因为&&它遵循true. 因此,shell 退出。

关于E:

与C 的推理相同:是最后一个或 后面true的命令。因此,shell 不会退出。&&||

关于F:

与D类似:这是一个&&||列表。我们必须查看最后一个&&或后面的命令||。同样,该命令false确实具有非零状态。但这一次没关系,因为第一个命令也是false如此。由于它是一个列表,因此不会评估&&第二个。false因此,shell 不会退出。

关于G:

CE的推理相同:是最后一个 或true后面的命令。因此,shell 不会退出。&&||

关于H:

这是一个&&||列表。我们必须查看最后一个&&或后面的命令||。该命令false确实具有非零状态,但不会对其进行评估,因为||前面带有true。因此,shell 不会退出。

关于我:

CEG的推理相同:是最后一个或true后面的命令。因此,shell 不会退出。&&||

关于J:

这是一个&&||列表。我们必须查看最后一个&&或后面的命令||。该命令false确实具有非零状态。由于||前面的false第二个false将被评估。因此,shell 确实退出了。


您应该能够将这些测试用例应用到您的案例中:true && false && true&&由于最后一个or后面的命令||true具有非零状态,因此无论&&or之前是什么||,shell 都不会退出。