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

如何判断bash脚本中的任何命令是否失败(非零退出状态),bash,sh,exit-code,exitstatus,Bash,Sh,Exit Code,Exitstatus,我想知道bash脚本中是否有任何命令以非零状态退出 我想要类似于set-e功能的东西,只是我不想在命令以非零状态退出时退出。我想让它运行整个脚本,然后我想知道: a) 退出状态为0的所有命令均已退出 -或- b) 一个或多个命令以非零状态退出 e、 g.鉴于以下情况: #!/bin/bash command1 # exits with status 1 command2 # exits with status 0 command3 # exits with status 0 我希望这

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

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

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


e、 g.鉴于以下情况:

#!/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

我不确定是否有现成的解决方案满足您的要求。我将编写如下函数:

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"
如果需要,可以增强该功能,将所有失败的命令存储在一个数组中,并在最后打印出来



您可能想查看这篇文章以了解更多信息:

对于可以执行此操作的每个命令:

if ! Command1 ; then an_error=1; fi 
并对所有命令重复此操作

最后,如果其中任何一个失败,则_错误将为1


如果需要失败计数,请在开始时将_错误设置为0,然后执行$(_错误++)。您可以将命令列表放入数组中,然后在命令上循环,而不是_error=1。任何返回错误代码的操作都会保留结果以供以后查看

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,则命令列表中没有错误


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

您可以尝试使用
调试
伪信号的陷阱,例如

trap '(( $? && ++errcount ))' DEBUG
执行
调试
陷阱

在每个简单命令之前,
for
命令、
case
命令、
select
命令、每个算术
for
命令,以及在shell函数中执行第一个命令之前

(引自)

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

#!/usr/bin/env bash

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

true
false
true

echo "Errors: $errcount"
返回
错误:1

#!/usr/bin/env bash

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

true
false
true
false

echo "Errors: $errcount"

打印
错误:2
。请注意,最后一条语句实际上需要解释第二个
false
,因为陷阱是在命令之前执行的,所以只有在执行
echo
行的陷阱时,才会检查第二个
false
的退出状态。

您可以使用
调试
陷阱,如:

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

# run commands here normally

exit $code
在错误上设置陷阱:

#!/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 $0
} >&2
trap report ERR

command1
command2
command3
exit $err

是的,我知道。这并不能直接回答我的问题。您可能暗示我可以修改脚本以保持所有退出状态的运行总数。这将是一个解决方案,但不是一个理想的解决方案,因为它需要在一个包含30条命令的脚本中添加30行代码,这使得它更难阅读,并且容易由于忘记将其添加到每个命令而导致人为错误。我认为可以创建一个函数,并将每个命令传递给该函数。还是挺麻烦的。我希望有一个简单的
set-e
类型的解决方案。@RobBednark这可能是你能做的最好的了。考虑一个命令,如<代码>((x=0))< /代码>。从技术上讲,这是一个失败的命令,因为包含的表达式的计算结果为0。任何自动化的东西都必须考虑到这一点。
set-e
的问题在于失败的命令不一定是错误。@GrishaLevit-你说得对-我更正了答案。看起来引用的主题总是让人困惑…完美,正是我想要的!谢谢@William_Pursell!