在Bash脚本中引发错误

Nav*_*mar 84 linux error-handling bash shell

我想在Bash脚本中引发错误,消息"Test cases Failed !!!".在Bash中如何做到这一点?

例如:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi
Run Code Online (Sandbox Code Playgroud)

For*_*Bru 99

这取决于您希望存储错误消息的位置.

您可以执行以下操作:

echo "Error!" > logfile.log
exit 125
Run Code Online (Sandbox Code Playgroud)

或者以下内容:

echo "Error!" 1>&2
exit 64
Run Code Online (Sandbox Code Playgroud)

当您引发异常时,您将停止程序的执行.

您还可以使用类似的exit xxx地方xxx,您可能想要返回到操作系统的错误代码(从0到255).这里12564只是你可以退出随机码.当您需要向操作系统指示程序异常停止时(例如发生错误),您需要将非零退出代码传递给exit.

正如@chepner 指出的那样,你可以这样做exit 1,这意味着一个未指明的错误.

  • 或者你可以把它发送到stderr,那里应该发生错误. (10认同)
  • 除非你有一个特定的含义,你应该只使用`exit 1`,按惯例,这意味着一个未指定的错误. (3认同)
  • @ user3078630,我刚刚编辑了我的答案.`1>&2`将成功 (2认同)

cod*_*ter 27

基本错误处理

如果您的测试用例运行器为失败的测试返回非零代码,您可以简单地写:

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi
Run Code Online (Sandbox Code Playgroud)

甚至更短:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi
Run Code Online (Sandbox Code Playgroud)

或者最短的:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
Run Code Online (Sandbox Code Playgroud)

要使用test_handler的退出代码退出:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
Run Code Online (Sandbox Code Playgroud)

高级错误处理

如果您想采用更全面的方法,可以使用错误处理程序:

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在运行测试用例后调用它:

run_test_case test_case_x
exit_if_error $? "Test case x failed"
Run Code Online (Sandbox Code Playgroud)

要么

run_test_case test_case_x || exit_if_error $? "Test case x failed"
Run Code Online (Sandbox Code Playgroud)

拥有错误处理程序的优点exit_if_error是:

  • 我们可以在一个地方标准化所有错误处理逻辑,例如记录,打印堆栈跟踪,通知,清理等
  • 通过使错误处理程序获取错误代码作为参数,我们可以使调用者免于if测试退出代码错误的块的混乱
  • 如果我们有一个信号处理程序(使用陷阱),我们可以从那里调用错误处理程序

相关文章


Ini*_*ian 6

您可以通过几种方法来解决此问题.假设您的一个要求是运行包含一些shell命令的shell脚本/函数,并检查脚本是否成功运行并在出现故障时抛出错误.

shell命令通常依赖于返回的退出代码,让shell知道它是否成功或由于某些意外事件而失败.

所以你想要做的就是这两个类别

  • 退出时出错
  • 退出并清除错误

根据您要执行的操作,可以使用shell选项.对于第一种情况,外壳提供了一个选项与set -e和第二你可以做一个trapEXIT

我应该exit在我的脚本/功能中使用吗?

使用exit通常会增强可读性在某些例程中,一旦知道答案,就要立即退出调用例程.如果例程的定义方式一旦检测到错误就不需要进一步清理,则不立即退出意味着您必须编写更多代码.

因此,如果您需要对脚本执行清理操作以使脚本终止,则最好不要使用exit.

我应该set -e在退出时使用错误吗?

没有!

set -e是尝试向shell添加"自动错误检测".它的目标是在发生错误的任何时候使shell中止,但它带来了许多潜在的陷阱,例如,

  • 作为if测试的一部分的命令是免疫的.在该示例中,如果您希望它在test检查不存在的目录时中断,则不会,它会进入else条件

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
    
    Run Code Online (Sandbox Code Playgroud)
  • 除了最后一个管道之外的管道中的命令是免疫的.在下面的示例中,因为最近执行的(最右边)命令的退出代码被认为是(cat)并且它是成功的.这可以通过set -o pipefail选项设置来避免,但它仍然是一个警告.

    set -e
    somecommand that fails | cat -
    echo survived 
    
    Run Code Online (Sandbox Code Playgroud)

建议使用 - trap退出时

这一判决是,如果你希望能够处理的错误,而不是盲目地退出,而不是使用set -e,使用trapERR伪信号.

ERR陷阱没有运行代码当壳本身具有非零的错误代码退出,但是当由该外壳运行任何命令不是一个条件的一部分(如在如果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
}
Run Code Online (Sandbox Code Playgroud)

我们只是在失败的脚本之上使用此处理程序

trap cleanup ERR
Run Code Online (Sandbox Code Playgroud)

将这些放在一个简单的脚本false上,该脚本包含在第15行,您将获得的信息

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
Run Code Online (Sandbox Code Playgroud)

trap还提供了一些选项,不论错误的,只是运行壳完成清理工作(如你的shell脚本退出),在信号EXIT.您还可以同时捕获多个信号.可以在trap.1p - Linux手册页上找到要捕获的支持信号列表

另一件需要注意的事情是要理解,如果您正在处理子shell,则所提供的方法都不起作用,在这种情况下,您可能需要添加自己的错误处理.


Pau*_*ges 6

这是一个简单的陷阱,它打印任何失败的 STDERR 的最后一个参数,报告它失败的行,并以行号作为退出代码退出脚本。请注意,这些并不总是很好的想法,但这展示了一些您可以构建的创造性应用程序。

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
Run Code Online (Sandbox Code Playgroud)

我把它放在一个带有循环的脚本中来测试它。我只是检查一些随机数的命中;您可能会使用实际测试。如果我需要保释,我会用我想抛出的消息调用 false(触发陷阱)。

对于详细的功能,让陷阱调用一个处理函数。如果您需要进行更多清理等,您始终可以在 arg ($_) 上使用 case 语句。为 var 分配一点语法糖 -

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false

while :
do x=$(( $RANDOM % 10 ))
   case "$x" in
   0) $throw "DIVISION BY ZERO" ;;
   3) $raise "MAGIC NUMBER"     ;;
   *) echo got $x               ;;
   esac
done
Run Code Online (Sandbox Code Playgroud)

示例输出:

# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6
Run Code Online (Sandbox Code Playgroud)

显然,你可以

runTest1 "Test1 fails" # message not used if it succeeds
Run Code Online (Sandbox Code Playgroud)

设计改进的空间很大。

缺点包括false不漂亮的事实(因此是糖),其他绊倒陷阱的事情可能看起来有点愚蠢。不过,我喜欢这种方法。


ala*_*oot 6

您有 2 个选择:将脚本的输出重定向到文件、在脚本中引入日志文件以及

  1. 将输出重定向到文件

这里您假设脚本输出所有必要的信息,包括警告和错误消息。然后,您可以将输出重定向到您选择的文件。

./runTests &> output.log
Run Code Online (Sandbox Code Playgroud)

上面的命令将标准输出和错误输出重定向到日志文件。

使用这种方法,您不必在脚本中引入日志文件,因此逻辑会更容易一些。

  1. 向脚本引入日志文件

在您的脚本中通过硬编码添加日志文件:

logFile='./path/to/log/file.log'
Run Code Online (Sandbox Code Playgroud)

或通过参数传递:

logFile="${1}"  # This assumes the first parameter to the script is the log file
Run Code Online (Sandbox Code Playgroud)

最好将执行时的时间戳添加到脚本顶部的日志文件中:

date '+%Y%-m%d-%H%M%S' >> "${logFile}"
Run Code Online (Sandbox Code Playgroud)

然后您可以将错误消息重定向到日志文件

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
fi
Run Code Online (Sandbox Code Playgroud)

这会将错误附加到日志文件并继续执行。如果您想在发生严重错误时停止执行,可以使用exit以下脚本:

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
    # Clean up if needed
    exit 1;
fi
Run Code Online (Sandbox Code Playgroud)

请注意,exit 1表示程序由于未指定的错误而停止执行。如果您愿意,您可以自定义它。

使用这种方法,您可以自定义日志,并为脚本的每个组件提供不同的日志文件。


如果您有一个相对较小的脚本或想要执行别人的脚本而不修改它,第一种方法更适合。

如果您总是希望日志文件位于同一位置,那么这是更好的选择。此外,如果您创建了一个包含多个组件的大脚本,那么您可能希望以不同的方式记录每个部分,而第二种方法是您唯一的方法选项。