Bash函数忽略set -e命令

3
如何将一个函数作为“已测试的命令”运行,并在失败时执行操作,同时尽快中止该函数?请参考以下脚本。
#!/bin/bash -e

function foo() {
   echo Entering foo
   false
   echo Should not reach this line
}

foo || echo I want to see this line on failure in foo
foo

我得到的输出是:
Entering foo
Should not reach this line
Entering foo

虽然我希望得到

Entering foo
I want to see this line on failure in foo
Entering foo

我想我正在寻找的是将函数标记为未经测试的命令的方法。根据bash man页面。
 -e errexit
         Exit immediately if any untested command fails in non-interactive
         mode.  The exit status of a command is considered to be explicitly
         tested if the command is part of the list used to control an if,
         elif, while, or until; if the command is the left hand operand of
         an “&&” or “||” operator; or if the command is a pipeline preceded
         by the ! operator.  If a shell function is executed and its exit
         status is explicitly tested, all commands of the function are con‐
         sidered to be tested as well.

编辑 期望的输出是错误的。已修改以更清晰。


2个回答

2

set -e 在第一次调用 foo 时被禁用,因为它在 || 的左侧。

另外,除非 foo 中的最后一个 echo 失败(它决定了函数的退出状态),否则您永远不会看到输出字符串 I want to see this ...

foo() {
    echo Entering foo
    false && echo Should not reach this line
}

foo || echo I want to see this line on failure in foo
foo

上述输出(带或不带set -x
Entering foo
I want to see this line on failure in foo
Entering foo

现在,falsefoo 中最后执行的语句。

如果我在foo函数中有一些复杂的逻辑,如何使其在任何命令失败时返回,而不仅仅是在最后一行返回? - Tzafrir
@Tzafrir 不要在 || 的左边使用 foo - Kusalananda

0

最终我将相关代码封装在下面的实用函数中。

#!/bin/bash -e

# Runs given code aborting on first error and taking desired action on failure
# $1 code to invoke, can be expression or function name
# $2 error handling code, can be function name or expressions
function saferun {
  set +e
  (set -E ; trap 'exit 1' ERR ; eval $1) 
  [ $? -ne 0 ] && eval $2
  set -e
}

function foo() {
   echo Entering foo
   false
   echo Should not reach this line
}

saferun foo "echo I want to see this line on failure in foo"
foo

让我们来分解一下:

  • set +eset -e 用于在出现错误时抑制失败,否则脚本将在第一个错误处退出
  • trap 用于在任何错误时中止执行(而不是使用 set -e),() 用于在子shell 中运行给定的代码,因此外部脚本将在失败后继续运行,并且 set -E 用于将陷阱传递到子shell。所以 (set -E; trap 'exit 1' ERR; eval $1) 运行给定的代码/函数,在第一个错误处中止,同时不退出整个脚本
  • $? -ne 0 检查失败,eval $2 运行错误处理代码

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接