关于bash:为什么set -e; true && false && true 不退出

Why does set -e; true && false && true not exit?

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

1
2
3
4
5
6
7
8
9
#!/usr/bin/env bash

set -e

echo"a"
echo"b"
echo"about to fail" && /bin/false && echo"foo"
echo"c"
echo"d"

印刷品:

1
2
3
4
5
6
$ ./foo.sh
a
b
about to fail
c
d

删除EDOCX1[1]确实会停止脚本;但是为什么呢?


因为这个答案不够具体。

它应该说(加粗文本是我的补充):

# Any subsequent simple commands which fail will cause the shell script to exit immediately

因为手册页上写着:

1
2
3
4
5
6
7
8
-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.

SHELL GRAMMAR则急剧扩张:

1
2
3
4
5
6
7
8
9
10
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.


为了简化etanreisner的详细答案,set -e只存在"未捕获"错误。在你的情况下:

1
echo"about to fail" && /bin/false && echo"foo"

失败代码/bin/false后接&&测试其退出代码。由于&&测试退出代码,假设程序员知道他在做什么,并且预期这个命令可能会失败。因此,脚本不退出。

相比之下,考虑:

1
echo"about to fail" && /bin/false

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

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

考虑:

1
2
set -e
echo"about to fail" && /bin/false ; echo"foo"

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


我在bash脚本中遇到了set -e,但对于在&&||列表中的最后一个&&||之后对命令的评估,我无法理解。我从http://man7.org/linux/man-pages/man1/bash.1.html了解关于set -e的以下引用:

The shell does not exit if the command that fails is (...) part of
any command executed in a && or || list except the command
following the final && or || (...)

为了测试这个,我写了一个小的bash脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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""

它打印:

1
ACEFGHI

关于A:

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

关于B:

false具有非零状态,不属于&&||列表的一部分。因此,外壳退出。

关于C:

这是一个&&||列表。我们必须查看最后一个&&||之后的命令。命令是true,它没有非零状态。所以,不管命令是否被评估,shell都不会退出。

关于D:

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

关于E:

与c:true的推理相同,是最后一个&&||之后的命令。因此,外壳不会退出。

关于F:

类似于d:这是一个&&||列表。我们必须查看最后一个&&||之后的命令。同样,命令是false,它具有非零状态。但这次没关系,因为第一个命令也是false。因为它是一个&&列表,所以第二个false不会被评估。因此,外壳不会退出。

关于G:

与c或e的推理相同:true是最后一个&&||之后的命令。因此,外壳不会退出。

关于H:

这是一个&&||列表。我们必须查看最后一个&&||之后的命令。这个命令是false,它确实具有非零状态,但由于||前面加了true,所以不会对它进行评估。因此,外壳不会退出。

关于我:

与c、e或g的推理相同:true是最后一个&&||之后的命令。因此,外壳不会退出。

关于J:

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

您应该能够将这些测试用例应用于您的案例:true && false && true。由于最后一个&&||之后的命令是true,它没有非零状态,所以不管&&||之前是什么,外壳都不会退出。