如何判断 bash 脚本中的任何命令是否失败(非零退出状态)

How to tell if any command in bash script failed (non-zero exit status)

我想知道 bash 脚本中是否有任何命令以非零状态退出。

我想要类似于 set -e 功能的东西,只是我不希望它在命令以非零状态退出时退出。我想要 运行 整个脚本,然后我想知道:

a) 所有命令以退出状态 0 退出
- 或 -
b) 一个或多个命令以非零状态退出


例如,给定以下内容:

#!/bin/bash

command1  # exits with status 1
command2  # exits with status 0
command3  # exits with status 0

我想要所有三个命令 运行。在 运行 脚本之后,我想要一个指示,表明至少有一个命令以非零状态退出。

你在 bash 中有魔法变量 $?,它告诉最后一个命令的退出代码:

#!/bin/bash

command1  # exits with status 1
C1_output=$?   # will be 1
command2  # exits with status 0
C2_output=$?   # will be 0
command3  # exits with status 0
C3_output=$?   # will be 0

我不确定是否有满足您要求的 ready-made 解决方案。我会写一个这样的函数:

function run_cmd_with_check() {
  "$@"
  [[ $? -ne 0 ]] && ((non_zero++))
}

然后,使用函数运行所有需要跟踪的命令:

run_cmd_with_check command1
run_cmd_with_check command2
run_cmd_with_check command3
printf "$non_zero commands exited with non-zero exit code\n"

如果需要,可以增强该功能,将所有失败的命令存储在一个数组中,最后打印出来。


您可能想看看这个 post 以获取更多信息:Error handling in Bash

对于每个命令,您可以这样做:

if ! Command1 ; then an_error=1; fi 

并对所有命令重复此操作

最后 an_error 如果其中任何一个失败,则为 1。

如果你想计算失败次数,请在开始时将 an_error 设置为 0,然后执行 $((an_error++))。而不是 an_error=1

您可以将命令列表放入一个数组中,然后循环执行这些命令。任何 return 错误代码都可以保留结果供以后查看。

declare -A results

commands=("your" "commands")

for cmd in "${commands[@]}"; do 
    out=$($cmd 2>&1)
    [[ $? -eq 0 ]] || results[$cmd]="$out"
done    

然后查看任何非零退出代码:

for cmd in "${!results[@]}"; do echo "$cmd = ${results[$cmd]}"; done

如果 results 的长度为 0,则您的命令列表中没有错误。

这需要 Bash 4+(对于关联数组)

您可以尝试对 DEBUG 伪信号做一些陷阱,例如

trap '(( $? && ++errcount ))' DEBUG

执行DEBUG陷阱

before every simple command, for command, case command, select command, every arithmetic for command, and before the first command executes in a shell function

(引自 manual)。

因此,如果您添加此陷阱并将其作为打印错误计数的最后命令,您将获得正确的值:

#!/usr/bin/env bash

trap '(( $? && ++errcount ))' DEBUG

true
false
true

echo "Errors: $errcount"

returns Errors: 1

#!/usr/bin/env bash

trap '(( $? && ++errcount ))' DEBUG

true
false
true
false

echo "Errors: $errcount"

打印 Errors: 2。请注意,实际上需要最后一条语句来说明第二个 false,因为陷阱是在 命令之前 执行的,因此第二个 false 的退出状态是仅在执行 echo 行的陷阱时检查。

您可以像这样使用 DEBUG 陷阱:

trap 'code+=$?' DEBUG
code=0

# run commands here normally

exit $code

在 ERR 上设置陷阱:

#!/bin/bash

err=0
trap 'err=1' ERR

command1
command2
command3
test $err = 0 # Return non-zero if any command failed

您甚至可以进行一些自省以获取有关错误发生位置的数据:

#!/bin/bash
for i in 1 2 3; do
        eval "command$i() { echo command$i; test $i != 2; }"
done

err=0
report() {
        err=1
        echo -n "error at line ${BASH_LINENO[0]}, in call to "
        sed -n ${BASH_LINENO[0]}p [=11=]
} >&2
trap report ERR

command1
command2
command3
exit $err