【问题标题】:How to tell if any command in bash script failed (non-zero exit status)如何判断 bash 脚本中的任何命令是否失败(非零退出状态)
【发布时间】:2017-07-02 06:17:34
【问题描述】:

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

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

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


例如,给定以下内容:

#!/bin/bash

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

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

【问题讨论】:

    标签: bash sh exit-code exitstatus


    【解决方案1】:

    设置 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 $0
    } >&2
    trap report ERR
    
    command1
    command2
    command3
    exit $err
    

    【讨论】:

    • 完美,正是我想要的!谢谢@William_Pursell!
    【解决方案2】:

    您可以尝试对DEBUG 伪信号设置陷阱,例如

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

    DEBUG 陷阱被执行

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

    (引自manual)。

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

    #!/usr/bin/env bash
    
    trap '(( $? && ++errcount ))' DEBUG
    
    true
    false
    true
    
    echo "Errors: $errcount"
    

    返回Errors: 1

    #!/usr/bin/env bash
    
    trap '(( $? && ++errcount ))' DEBUG
    
    true
    false
    true
    false
    
    echo "Errors: $errcount"
    

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

    【讨论】:

      【解决方案3】:

      我不确定是否有现成的解决方案可以满足您的要求。我会写一个这样的函数:

      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"
      

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


      您可能想查看此帖子以获取更多信息:Error handling in Bash

      【讨论】:

      • @GrishaLevit - 你是对的 - 我更正了答案。看起来引用主题总是有点混乱......
      【解决方案4】:

      你在 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
      

      【讨论】:

      • 是的,我知道这一点。这并不能直接回答我的问题。您可能暗示我可以修改脚本以保持所有退出状态的运行总数。这将是一个解决方案,但不是一个理想的解决方案,因为它需要将 30 行代码添加到包含 30 个命令的脚本中,使其更难阅读,并且由于忘记将其添加到每个脚本中而容易出现人为错误命令。我想可以创建一个函数并将每个命令传递给该函数。还是比较麻烦的。我希望有一个简单的set -e 类型的解决方案。
      • @RobBednark 这可能是你能做的最好的了。考虑像@​​987654324@ 这样的命令。从技术上讲,这是一个失败的命令,因为包含的表达式的计算结果为 0。任何自动化的东西都必须考虑到这样的事情。 set -e 的问题在于,失败的命令不一定是错误。
      【解决方案5】:

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

      if ! Command1 ; then an_error=1; fi 
      

      对所有命令重复此操作

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

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

      【讨论】:

        【解决方案6】:

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

        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+(用于关联数组)

        【讨论】:

          【解决方案7】:

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

          trap 'code+=$?' DEBUG
          code=0
          
          # run commands here normally
          
          exit $code
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-10-28
            • 2017-03-08
            • 2021-12-21
            • 1970-01-01
            • 2021-05-10
            • 1970-01-01
            相关资源
            最近更新 更多