Bash 脚本:如何确保脚本在使用“或”(||) 条件调用的函数中出现任何错误时退出?

Posted

技术标签:

【中文标题】Bash 脚本:如何确保脚本在使用“或”(||) 条件调用的函数中出现任何错误时退出?【英文标题】:Bash scripting: How to ensure script exits on any error in function called with an "or" (||) condition? 【发布时间】:2019-08-10 23:55:17 【问题描述】:

这是一个示例脚本:

#!/bin/bash
set -euo pipefail

function_test () 
        echo "Everything still ok."
        thisdoesnotexist
        echo "It continues running..."


function_test ||  echo "Error occured in function_test."; exit 1; 

我希望此脚本在“thisdoesnotexist”上退出,因为 set -e 已激活。错误消息“function_test 中发生错误”。应该出现,或者至少应该退出脚本。相反,脚本继续运行:

$ ./testscript.sh 
Everything still ok.
./testscript.sh: Zeile 6: thisdoesnotexist: Kommando nicht gefunden.
It continues running...

根据set 的手册,我认为这是因为我在“或”(||) 上下文中使用该函数:

立即退出 [...] 除非失败的命令是 until 或 while 循环,if 语句的一部分,&& 或 || 的一部分 列表,[...]

在脚本中处理此问题的最佳方法是什么?我删除了“||”并使用了这样的错误陷阱:

#!/bin/bash
set -Eeuo pipefail

errorhandler () 
        echo "Errorhandler called. Exiting."
        exit 1


trap "errorhandler" ERR

function_test () 
        echo "Everything still ok."
        thisdoesnotexist
        echo "It continues running..."


function_test

哪个有效:

$ ./testscript.sh 
Everything still ok.
./testscript.sh: Zeile 13: thisdoesnotexist: Kommando nicht gefunden.
Errorhandler called. Exiting.

在这种情况下,虽然似乎不可能输出用户友好的消息(例如,其中包含脚本中发生错误的步骤)。为此,我至少必须将函数名称传递给错误陷阱。我试过了,但没有找到可行的解决方案。

你有更好的建议吗?

【问题讨论】:

【参考方案1】:

function_test || echo "Error occured in function_test."; exit 1;

由于函数返回状态,OR 永远不会发生。该函数的最后一个 echo 语句导致返回状态为 0,因为它已成功运行。

如果您在不成功的命令上添加非零返回,那么它将导致 OR 语句正常运行。

function_test () 
        echo "Everything still ok."
        thisdoesnotexist || return 1;
        echo "It continues running..."

【讨论】:

【参考方案2】:

不是很消耗,但在某些情况下更可取。导出标志和您的函数,并在子外壳中运行它。处理 bash 错误值。

#!/bin/bash
set -euo pipefail

function_test () 
    echo "Everything still ok."
    thisdoesnotexist
    echo "It continues running..."


export -f function_test
export SHELLOPTS
bash -c function_test ||  echo "Error occured in function_test."; exit 1; 

【讨论】:

【参考方案3】:

如果你想在出错的情况下做一些事情并且你知道可能会出错,那么:

function_test () 
        echo "Everything still ok."
        thisdoesnotexist || 
          # handle thisdoesnotexist failure
          Echo "thisdoesnotexist failed" >&2
          exit 1
        
        echo "It continues running..."
        if ! thisdoesnotexisteither; then
          # Handle fail for thisdoesnotexisteither
          Echo "thisdoesnotexisteither failed" >&2
          exit 1
        fi

现在为了测试 Bash 中的错误处理,您可以使用这个智能错误功能:

#!/usr/bin/env bash

# Generate an error
# @Params
# $1: Numeric error return code
# $2[@]: Messages
# @Output
# &1: Generic success message
# &2: Generic or passed error messages
# @Return
# $?: Intended return code
erroring() 
  local rc=1
  if [ "$#" -gt 0 ]; then
    rc="$1"
    shift
    if printf '%d' "$rc" >/dev/null 2>&1; then
      # rc is an integer
      if [ "$rc" -gt 0 ]; then
        printf $"Intended failure with code: %d\\n" "$rc" >&2
        if [ "$#" -gt 0 ]; then
          # There are supplied messages to print"
          printf $"%d messages:\\n" "$#" >&2
          printf '%q\n' "$@" >&2
        fi
      else
        echo $"Intended success"
      fi
    else
      printf $"Invalid non-numeric return code parameter: %q\\n" "$rc" >&2
      rc=7
    fi
  else
    erroring "$rc"
  fi
  return "$rc"

【讨论】:

最后一个printf $'Invalid - 这是printf $"Invalid ?递归调用erroring 有什么意义? if [ "$#" -gt 0 ]; then rc=$1; else rc=1; fi 不是更简单吗? 这是 erroring 函数失败的糖,因为它传递了一个非数字返回码。就像打电话给erroring "Error message" 而不是打电话给erroring 1 "Error message"

以上是关于Bash 脚本:如何确保脚本在使用“或”(||) 条件调用的函数中出现任何错误时退出?的主要内容,如果未能解决你的问题,请参考以下文章

Bash基础——Shell脚本内部常用环境变量

如何在不使用“sh”或“bash”命令的情况下运行 shell 脚本?

如何在 Azure CLI 或 Bash 脚本中获取天蓝色存储帐户的大小?

如何在bash脚本中传递两个参数或参数[重复]

BASH 脚本使用输入和标准错误来长列表目录

如何将执行命令从 bash 脚本转换为 C?或如何在“W”模式下正确使用 popen()?