【问题标题】:Bash ignoring error for a particular commandBash 忽略特定命令的错误
【发布时间】:2022-01-25 06:59:21
【问题描述】:

我正在使用以下选项

set -o pipefail
set -e

在 bash 脚本中停止执行错误。我有大约 100 行脚本正在执行,我不想检查脚本中每一行的返回码。

但是对于一个特定的命令,我想忽略错误。我该怎么做?

【问题讨论】:

    标签: linux bash


    【解决方案1】:

    这里没有适合我的解决方案,所以我找到了另一个解决方案:

    set +e
    find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
    find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
    set -e
    

    这对 CI 和 CD 很有用。这样会打印错误消息,但整个脚本会继续执行。

    【讨论】:

      【解决方案2】:

      感谢上面的简单解决方案:

      <particular_script/command> || true
      

      以下构造可用于脚本步骤的附加操作/故障排除和附加流控制选项:

      if <particular_script/command>
      then
         echo "<particular_script/command> is fine!"
      else
         echo "<particular_script/command> failed!"
         #exit 1
      fi
      

      如果需要,我们可以阻止进一步的操作和exit 1

      【讨论】:

        【解决方案3】:
        output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
        echo $output
        echo $exit_status
        

        使用它创建日志文件的示例

        log_event(){
        timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
        echo -e "($timestamp) $event" >> "$log_file"
        }
        
        output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
        
        if [ "$exit_status" = 0 ]
            then
                event="$output"
                log_event
            else
                event="ERROR $output"
                log_event
        fi
        

        【讨论】:

          【解决方案4】:

          解决办法:

          particular_script || true
          

          例子:

          $ cat /tmp/1.sh
          particular_script()
          {
              false
          }
          
          set -e
          
          echo one
          particular_script || true
          echo two
          particular_script
          echo three
          
          $ bash /tmp/1.sh
          one
          two
          

          three 永远不会被打印出来。

          另外,我想补充一点,当pipefail 开启时, shell 认为整个管道具有非零退出代码就足够了 当管道中的命令之一具有非零退出代码时(pipefail 关闭,它必须是最后一个)。

          $ set -o pipefail
          $ false | true ; echo $?
          1
          $ set +o pipefail
          $ false | true ; echo $?
          0
          

          【讨论】:

          • +1。正如 Bash 参考手册 explains,“当设置了 -e 属性时,“shell 不会退出”“如果失败的命令是紧跟在 whileuntil 关键字之后的命令列表的一部分,则if 语句中的测试,在 &amp;&amp;|| 列表中执行的任何命令的一部分,除了最后一个 &amp;&amp;|| 之后的命令,管道中除最后一个以外的任何命令,或者如果命令的返回状态正在与 ! 反转。"
          • @IgorChubin 我不知道为什么,但这不起作用 output=(ldd $2/bin/* || true) | grep "not found" | wc -l 当 ldd 返回失败时脚本在此行之后终止
          • (ldd $2/bin/* || true) | grep "not found" | wc -l || true
          • 因为set -o pipefail。当grep 什么都没有找到时,它返回非零退出代码,shell 认为整个管道都有非零退出代码就足够了。
          • 如果您想要保留返回值(不退出)的选项,请尝试 mycommand && true。这允许您在后续步骤中检查返回码并以编程方式处理它。
          【解决方案5】:

          如果您想防止脚本失败收集返回码:

          command () {
              return 1  # or 0 for success
          }
          
          set -e
          
          command && returncode=$? || returncode=$?
          echo $returncode
          

          returncode无论命令成功还是失败都会被收集。

          【讨论】:

          • 你是如何内联的?
          【解决方案6】:

          不要停止,还要保存退出状态

          以防万一您希望脚本在特定命令失败时不停止,并且您还想保存失败命令的错误代码:

          set -e
          EXIT_CODE=0
          command || EXIT_CODE=$?
          echo $EXIT_CODE
          

          【讨论】:

          • 当命令返回 0 时,EXIT_CODE 未设置为零。但会捕获非零退出代码。知道为什么吗?
          • @Ankita13 因为如果它为零,则第一个 command 成功,需要运行 || 之后的内容。将其读作:如果command 失败,则执行EXIT_CODE=$?。您可能可以只做command || echo "$?" 或使用“陷阱”进行更详细的调试。看到这个->stackoverflow.com/a/6110446/10737630
          • 我认为这可能是最好的答案。这就是我一直在寻找的东西。我不希望 shell 退出,但我确实想知道错误,所以我可以回应。非常感谢!
          • 我已经使用这种技术手动强制停止 Gitlab CI 作业,因为即使退出代码为 1,我正在运行的命令似乎也会挂起,所以我最终自己退出了。
          【解决方案7】:

          我有点喜欢这个解决方案:

          : `particular_script`
          

          执行反引号之间的命令/脚本,并将其输出馈送到命令“:”(相当于“true”)

          $ false
          $ echo $?
          1
          $ : `false`
          $ echo $?
          0
          

          编辑:修正了难看的错字

          【讨论】:

            【解决方案8】:

            虽然|| true 是首选,但你也可以这样做

            var=$(echo $(exit 1)) # it shouldn't fail
            

            【讨论】:

              【解决方案9】:

              我在使用 CLI 工具时一直在使用下面的 sn-p,我想知道是否存在某些资源,但我不关心输出。

              if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
                  echo "none exist actually exist!"
              fi
              

              【讨论】:

              • 这似乎是一种非常迂回且价格适中的说法if [ -r not_exist ]
              【解决方案10】:

              除了“返回 true”,您还可以使用“noop”或 null 实用程序(如 POSIX specs 中所述): 并且只是“什么也不做”。你会省下几封信。 :)

              #!/usr/bin/env bash
              set -e
              man nonexistentghing || :
              echo "It's ok.."
              

              【讨论】:

              • 虽然 ||: 不像 || 那样清晰真的(这可能会使非专家用户感到困惑),我喜欢简洁
              • 此变体不返回任何在 CI 中可能很重要的文本。
              • 很好,但是有没有其他方法可以更清楚地返回 null?
              【解决方案11】:

              更简洁:

              ! particular_script
              

              来自POSIX specification 关于set -e(强调我的):

              启用此选项时,如果简单命令由于 Shell 错误的后果中列出的任何原因而失败或返回退出状态值 >0,并且在一段时间、直到或如果之后不属于复合列表的一部分关键字,并且不是 AND 或 OR 列表的一部分,也不是前面有 ! 的管道 保留字,则shell将立即退出。

              【讨论】:

              • 这只是反转命令的退出代码,因此成功完成的命令将返回 1 而不是 0 并使用 -e 失败脚本。
              • 我的理解是!无论如何都会阻止shell退出。 This script 运行时显示消息Still alive!,表示脚本运行完成。您是否看到了不同的行为?
              • 你是对的,它会反转退出状态,但是当命令以 0 或 1 结尾时不会崩溃脚本。我不专心。无论如何,感谢您引用文档,我将我的表达式放在if 子句中并解决了问题。
              • 这是 IMO 的最佳解决方案,因为在许多情况下,我想忽略 particular_script 的返回值,但如果 particular script 是一个我确实想要的函数忽略函数内语句的返回值。 || 解决方案将忽略 particular_script 函数体内的所有返回码,这通常不是您想要的 (stackoverflow.com/a/37191242/985292)。
              【解决方案12】:

              只需在要忽略错误的命令后添加|| true

              【讨论】:

              • 我认为添加这一点很重要:此方法将允许响应代码和错误消息持续存在,而“!”下面概述的方法将更改响应代码,因此不会产生错误。这在使用set -e 并尝试捕获错误消息时很重要:例如set -e; TEST=$(foo 2&gt;&amp;1 || true); echo $TEST
              • @Spanky 我不太明白你的例子。你set -e,将找不到的bash命令从stderr重定向到$TEST。这如何保留响应代码?
              • @Spanky 你在尝试做这样的事情吗set -e; TEST=$(foo 2&gt;&amp;1) || printf "ERROR $?: $TEST\n"
              猜你喜欢
              • 2016-04-21
              • 1970-01-01
              • 2018-06-20
              • 1970-01-01
              • 2015-09-28
              • 2010-10-20
              • 2017-03-29
              相关资源
              最近更新 更多