0

虽然Bash 手册页指出:

如果失败的命令是 ... 在 && 或 || 中执行的命令的一部分,则不会执行 ERR 陷阱 列表 ...

我希望 subshel​​l 中的代码处于不同的上下文中,并且不受上述限制。下面的代码表明,即使是 subshel​​l 也不能免受此限制:

#!/bin/bash

main()
{
   local Arg="$1"
   (
      set -e
      echo "In main: $Arg"
      trap MyTrap ERR
      $(exit 1)
      echo "Should not get here"
   )

   return 1
}

MyTrap()
{
   echo "In MyTrap"
}

main 1
[[ $? -eq 0 ]] || echo "failed"
echo
main 2 || echo "failed"

上面的代码有以下输出:

In main: 1
In MyTrap
failed

In main: 2
Should not get here
failed

我目前的解决方法是使用文件持久性来保存错误状态,MyTrap然后在调用者中检查返回码。例如:

MyTrap()
{
   echo "_ERROR_=$?" > $HOME/.persist
   echo "In MyTrap"
}

main 1
[[ -f $HOME/.persist ]] && . $HOME/.persist || _ERROR_=0
[[ $_ERROR_ -eq 0 ]] || echo "failed"

上面的输出现在是:

In main: 1
In MyTrap
failed

所以,问题是:是否可以找到一种方法来执行 set -e/ERR 子shell 捕获,它不受上述解决方法的影响&&和限制?||

注:此问题适用于:

  • Bash 4.2 及更高版本
  • Red Hat 7、CentOS 7 和相关发行版(即不是 Debian 等)
  • 不得使用第三方软件。软件包必须可用,例如,通过 [CentOS-7-x86_64-DVD-1611.iso][2] 存储库 iso 中的 OS-provider 软件包(对于 RHEL 7、Fedora 7 等类似)。
4

1 回答 1

0

此代码不是解决方案。这是一个修改后的版本,您可以对其进行测试以提供一些想法。

#!/bin/bash

main()
{
   local Arg="$1"
   (
      echo "In main: $Arg"
      $(exit 1)
      echo "Should not get here"
   )

   return 1
}

MyTrap()
{
   echo "In MyTrap"
   exit 1
}

set -o errtrace
set -o functrace
trap MyTrap ERR

main 1
[[ $? -eq 0 ]] || echo "failed"
echo
main 2 || echo "failed"

它不会做你想做的事情(我假设),因为缺少一些重要的东西,太复杂了,无法在帖子中解释,但有一些关键的事情。

如果要在所有脚本中统一处理错误,则必须执行以下操作。

  • 在顶层设置陷阱,不要使用set -e
  • 使用导致子shell和函数继承陷阱的shell选项(如示例所示)
  • 创建一个框架,在其中区分“预期”错误(明确处理为某种异常,而不是作为返回码)和“意外”异常(错误),它们将被捕获。
  • 永远不要直接使用逻辑运算符(除了测试结构或您认为足够安全的其他简单语句)
  • 收集异常后执行逻辑测试,而不是对返回码进行测试。

构建该框架非常棘手,但可行(我让它在几十个非常复杂的脚本上完美运行)。一旦你完成了它,如果你选择盲目地测试一个命令(没有明确地处理异常),如果它失败了,你可以选择让你的脚本在陷阱(exit)中崩溃,而不是在你的脚本处于不稳定状态时继续执行。

这可能会导致轻微的性能损失(至少是我这样做的方式,在使用try异常处理的每个语句之前使用一个特殊函数),并且肯定需要大量的编码纪律,但在我的情况下,它让我更加以合理的信心高效地构建复杂的脚本,任何错误的位置(不是我有任何错误......)将更容易查明。

于 2017-01-23T00:24:22.757 回答