还有更多方法可以解决这个问题。假设您的要求之一是运行包含一些 shell 命令的 shell 脚本/函数,并检查脚本是否成功运行并在失败时抛出错误。
shell 命令通常依赖于返回的退出代码来让 shell 知道它是成功还是由于一些意外事件而失败。
所以你想做的事情就属于这两类
根据您要执行的操作,可以使用多种 shell 选项。对于第一种情况,shell 提供了一个带有set -e 的选项,对于第二种情况,您可以在EXIT 上执行trap
我应该在我的脚本/函数中使用exit 吗?
使用exit 通常会增强可读性 在某些例程中,一旦知道答案,就想立即退出调用例程。如果例程的定义方式是一旦检测到错误就不需要任何进一步的清理,那么不立即退出意味着您必须编写更多代码。
因此,如果您需要对脚本执行清理操作以使脚本终止干净,则最好不使用exit。
我应该在退出时使用set -e 处理错误吗?
不!
set -e 试图在 shell 中添加“自动错误检测”。它的目标是在发生错误时使 shell 中止,但它带来了很多潜在的陷阱,例如,
-
if 测试中的命令是免疫的。在示例中,如果您希望它在test 检查不存在的目录时中断,它不会,它会进入 else 条件
set -e
f() { test -d nosuchdir && echo no dir; }
f
echo survived
-
管道中除最后一个以外的命令是免疫的。在下面的示例中,因为考虑了最近执行(最右边)命令的退出代码(cat)并且它是成功的。这可以通过设置set -o pipefail 选项来避免,但它仍然是一个警告。
set -e
somecommand that fails | cat -
echo survived
推荐使用 - 退出时trap
结论是,如果您希望能够处理错误而不是盲目退出,而不是使用set -e,请在ERR 伪信号上使用trap。
ERR 陷阱不是在 shell 本身以非零错误代码退出时运行代码,而是在该 shell 运行的任何不属于条件的命令时运行代码(如 if cmd,或cmd ||) 以非零退出状态退出。
一般的做法是我们定义一个陷阱处理程序来提供关于哪一行和什么导致退出的附加调试信息。请记住,导致ERR 信号的最后一个命令的退出代码此时仍然可用。
cleanup() {
exitcode=$?
printf 'error condition hit\n' 1>&2
printf 'exit code returned: %s\n' "$exitcode"
printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
我们只是在失败的脚本之上使用下面的这个处理程序
trap cleanup ERR
将这些放在一个简单的脚本中,该脚本在第 15 行包含 false,您将获得如下信息
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
trap 还提供了选项,无论错误如何,都可以在 shell 完成时(例如,你的 shell 脚本退出),在信号 EXIT 上运行清理。您还可以同时捕获多个信号。可以在trap.1p - Linux manual page 上找到要捕获的受支持信号列表
要注意的另一件事是要了解,如果您正在处理涉及子外壳的任何提供的方法都不起作用,在这种情况下,您可能需要添加自己的错误处理。
-
在带有set -e 的子shell 上不起作用。 false 仅限于子 shell,永远不会传播到父 shell。在这里做错误处理,添加你自己的逻辑来做(false) || false
set -e
(false)
echo survived
-
trap 也会发生同样的情况。由于上述原因,下面的逻辑不起作用。
trap 'echo error' ERR
(false)