为什么设置-e;真实&&虚假&&真的不退出?

时间:2014-09-11 18:45:53

标签: bash

根据this accepted answer使用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"

打印:

$ ./foo.sh 
a
b
about to fail
c
d

删除echo "foo" 停止脚本;但为什么呢?

3 个答案:

答案 0 :(得分: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.

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.

答案 1 :(得分:5)

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

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

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

相比之下,请考虑:

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

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

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

考虑:

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

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

答案 2 :(得分:1)

我遇到了set -e的Bash脚本,但是在&&或{{||&&||之后的命令评估方面遇到了什么问题。 1}}列表。我知道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 ""

打印:

ACEFGHI

关于A:

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

关于B:

false确实具有非零状态,并且不属于&&||列表。因此,shell退出。

关于C:

这是&&||列表。我们必须查看上一个&&||之后的命令。命令为true,其状态不为非零。因此,无论是否评估命令都无关紧要 - shell不会退出。

关于D:

这是&&||列表。我们必须查看上一个&&||之后的命令。这次,命令为false,其状态为非零。因此,我们必须检查是否正在评估false - 确实是这种情况,因为&&跟随true。因此,shell退出。

关于E:

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

关于F:

D 相似:这是&&||列表。我们必须查看上一个&&||之后的命令。命令再次为false,其状态为非零。但这一次并不重要,因为第一个命令也是false。由于它是&&列表,因此不会评估第二个false。因此,shell不会退出。

关于G:

C E 相同的推理:true是上一个&&||之后的命令。因此,shell不会退出。

关于H:

这是&&||列表。我们必须查看上一个&&||之后的命令。此命令为false,其状态为非零,但由于||前面有true,因此无法对其进行评估。因此,shell不会退出。

关于我:

C E G 相同的推理:true是上一个&&之后的命令或||。因此,shell不会退出。

关于J:

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

您应该能够将这些测试用例应用于您的案例:true && false && true。由于上一个&&||之后的命令是true且状态不为零,因此&&之前的内容并不重要或者||,shell不会退出。