#!/bin/bash # ============================================================================ # 文件名: error_handler.sh # 描述: 统一的错误处理机制 # 作者: Cloud Tools Project # 版本: 1.0.0 # 依赖: logging.sh # 使用方法: source common/error_handler.sh # ============================================================================ # 确保加载日志库 if ! declare -f log_error >/dev/null 2>&1; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=./logging.sh source "${SCRIPT_DIR}/logging.sh" fi # 错误处理配置 ERROR_EXIT_ON_FAIL=${ERROR_EXIT_ON_FAIL:-true} ERROR_STACK_TRACE=${ERROR_STACK_TRACE:-false} # # 内部: 获取调用栈信息 # 返回: 格式化的调用栈字符串 # _error_get_stack_trace() { local frame=0 local line local func local src echo "调用栈:" while caller $frame; do ((frame++)) done | while read -r line func src; do echo " at $func ($src:$line)" done } # # 检查命令是否存在 # 参数: $1 - 命令名称 # 返回: 0=存在, 1=不存在 # 示例: check_command "git" || error_exit "git 未安装" # check_command() { local cmd="$1" if ! command -v "$cmd" &>/dev/null; then log_error "命令不存在: $cmd" return 1 fi return 0 } # # 检查文件是否存在 # 参数: $1 - 文件路径 # 返回: 0=存在, 1=不存在 # 示例: check_file "/path/to/file" || error_exit "文件不存在" # check_file() { local file="$1" if [[ ! -f "$file" ]]; then log_error "文件不存在: $file" return 1 fi return 0 } # # 检查目录是否存在 # 参数: $1 - 目录路径 # 返回: 0=存在, 1=不存在 # 示例: check_directory "/path/to/dir" || error_exit "目录不存在" # check_directory() { local dir="$1" if [[ ! -d "$dir" ]]; then log_error "目录不存在: $dir" return 1 fi return 0 } # # 检查变量是否为空 # 参数: # $1 - 变量值 # $2 - 变量名称 (可选,用于错误消息) # 返回: 0=非空, 1=为空 # 示例: check_not_empty "$VAR" "VAR" || error_exit "变量不能为空" # check_not_empty() { local value="$1" local var_name="${2:-变量}" if [[ -z "$value" ]]; then log_error "${var_name} 不能为空" return 1 fi return 0 } # # 检查是否以 root 权限运行 # 返回: 0=是root, 1=不是root # 示例: check_root || error_exit "此脚本需要 root 权限" # check_root() { if [[ $EUID -ne 0 ]]; then log_error "此脚本需要 root 权限运行" return 1 fi return 0 } # # 检查命令返回值 # 参数: # $1 - 命令返回值 ($?) # $2 - 错误消息 (可选) # 返回: 传入的返回值 # 示例: some_command; check_return $? "命令执行失败" # check_return() { local ret=$1 local msg="${2:-命令执行失败}" if [[ $ret -ne 0 ]]; then log_error "$msg (退出码: $ret)" if [[ "$ERROR_STACK_TRACE" == "true" ]]; then _error_get_stack_trace >&2 fi if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then exit "$ret" fi fi return "$ret" } # # 错误退出 # 参数: # $1 - 错误消息 # $2 - 退出码 (可选,默认: 1) # 示例: error_exit "配置文件不存在" 2 # error_exit() { local msg="$1" local exit_code="${2:-1}" log_error "$msg" if [[ "$ERROR_STACK_TRACE" == "true" ]]; then _error_get_stack_trace >&2 fi exit "$exit_code" } # # 执行命令并检查结果 # 参数: # $1 - 错误消息前缀 # $@ - 要执行的命令和参数 # 返回: 命令的返回值 # 示例: run_command "安装失败" apt-get install -y package # run_command() { local error_msg="$1" shift log_debug "执行命令: $*" if ! "$@"; then local ret=$? log_error "${error_msg}: $*" if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then exit "$ret" fi return "$ret" fi return 0 } # # 带重试的命令执行 # 参数: # $1 - 最大重试次数 # $2 - 重试间隔 (秒) # $3 - 错误消息前缀 # $@ - 要执行的命令和参数 # 返回: 命令的返回值 # 示例: retry_command 3 5 "连接失败" curl -f https://example.com # retry_command() { local max_retries=$1 local retry_delay=$2 local error_msg="$3" shift 3 local attempt=1 local ret while [[ $attempt -le $max_retries ]]; do log_debug "尝试执行 (第 $attempt/$max_retries 次): $*" if "$@"; then log_debug "命令执行成功" return 0 fi ret=$? if [[ $attempt -lt $max_retries ]]; then log_warning "${error_msg} (第 $attempt 次失败,将在 ${retry_delay}秒后重试)" sleep "$retry_delay" else log_error "${error_msg} (已重试 $max_retries 次)" fi ((attempt++)) done if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then exit "$ret" fi return "$ret" } # # 启用错误时退出 # 示例: enable_exit_on_error # enable_exit_on_error() { ERROR_EXIT_ON_FAIL=true set -e set -o pipefail } # # 禁用错误时退出 # 示例: disable_exit_on_error # disable_exit_on_error() { ERROR_EXIT_ON_FAIL=false set +e set +o pipefail } # # 启用调用栈跟踪 # 示例: enable_stack_trace # enable_stack_trace() { ERROR_STACK_TRACE=true } # # 禁用调用栈跟踪 # 示例: disable_stack_trace # disable_stack_trace() { ERROR_STACK_TRACE=false } # 设置 ERR 陷阱(可选) # trap '_error_handler $? $LINENO' ERR # # 内部: ERR 陷阱处理函数 # 参数: # $1 - 错误码 # $2 - 行号 # _error_handler() { local exit_code=$1 local line_number=$2 log_error "脚本在第 $line_number 行发生错误 (退出码: $exit_code)" if [[ "$ERROR_STACK_TRACE" == "true" ]]; then _error_get_stack_trace >&2 fi }