Linux 在Bash脚本中引发错误

Linux 在Bash脚本中引发错误,linux,bash,shell,error-handling,scripting,Linux,Bash,Shell,Error Handling,Scripting,我想在Bash脚本中引发一个错误,消息为“testcases Failed!!!”。如何在Bash中实现这一点 例如: if [ condition ]; then raise error "Test cases failed !!!" fi 这取决于要将错误消息存储在何处 您可以执行以下操作: echo "Error!" > logfile.log exit 125 echo "Error!" 1>&2 exit 64 或以下各项: echo "Error!"

我想在Bash脚本中引发一个错误,消息为“testcases Failed!!!”。如何在Bash中实现这一点

例如:

if [ condition ]; then
    raise error "Test cases failed !!!"
fi

这取决于要将错误消息存储在何处

您可以执行以下操作:

echo "Error!" > logfile.log
exit 125
echo "Error!" 1>&2
exit 64
或以下各项:

echo "Error!" > logfile.log
exit 125
echo "Error!" 1>&2
exit 64
当您引发异常时,您将停止程序的执行

您也可以使用类似于
exit xxx
的方法,其中
xxx
是您可能希望返回到操作系统的错误代码(从0到255)。这里的
125
64
只是可以退出的随机代码。当您需要向操作系统指示程序异常停止(例如发生错误)时,您需要将非零退出代码传递给
退出

作为@chepner,您可以执行
退出1
,这将意味着一个未指定的错误

基本错误处理 如果您的测试用例运行程序返回失败测试的结果,您只需编写:

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
甚至更短:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi
或最短的:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
要使用test_处理程序的退出代码退出,请执行以下操作:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
高级错误处理 如果要采取更全面的方法,可以使用错误处理程序:

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_test_case test_case_x
exit_if_error $? "Test case x failed"

如果出现错误,则使用类似于
exit\u的错误处理程序的优点是:

  • 我们可以在一个地方标准化所有错误处理逻辑,如打印、通知、执行清理等
  • 通过使错误处理程序将错误代码作为参数获取,我们可以使调用方免受测试退出代码错误的
    if
    块的干扰
  • 如果我们有一个信号处理程序(使用),我们可以从那里调用错误处理程序
错误处理和日志库 下面是错误处理和日志记录的完整实现:


相关职位
  • 关于Bash黑客Wiki
  • 要向错误处理程序添加堆栈跟踪,您可能需要查看以下文章:

您还可以通过几种方法来解决此问题。假设您的需求之一是运行包含几个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 
    
建议使用-退出时使用陷阱 结论是,如果您希望能够处理错误而不是盲目退出,而不是使用
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
时运行清理即可。您还可以同时捕获多个信号。可在上找到要捕获的支持信号列表

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

  • 在具有
    set的子shell上-e
    不起作用。
    false
    仅限于子shell,永远不会传播到父shell。要在此处执行错误处理,请添加自己的逻辑以执行
    (false)| | false

    set -e
    (false)
    echo survived
    
  • # Usage: die [exit_code] [error message]
    die() {
      local code=$? now=$(date +%T.%N)
      if [ "$1" -ge 0 ] 2>/dev/null; then  # assume $1 is an error code if numeric
        code="$1"
        shift
      fi
      echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
      exit $code
    }
    
    if [ condition ]; then die 123 "condition not met"; fi
    # exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"
    
    $command |grep -q condition || die 1 "'$command' lacked 'condition'"
    # exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"
    
    $command || die
    # exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
    
    trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
    
    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
    
    # bash tst
    got 2
    got 8
    DIVISION BY ZERO at 6
    # echo $?
    6
    
    runTest1 "Test1 fails" # message not used if it succeeds
    
    ./runTests &> output.log
    
    logFile='./path/to/log/file.log'
    
    logFile="${1}"  # This assumes the first parameter to the script is the log file
    
    date '+%Y%-m%d-%H%M%S' >> "${logFile}"
    
    if [ condition ]; then
        echo "Test cases failed!!" >> "${logFile}"; 
    fi
    
    if [ condition ]; then
        echo "Test cases failed!!" >> "${logFile}"; 
        # Clean up if needed
        exit 1;
    fi