Shell错误处理Skill shell-error-handling

这个技能用于在Shell脚本中实现健壮的错误处理、清理例程和调试。它涵盖了退出代码检查、陷阱机制、错误报告、防御性编程和调试模式。关键词:Shell脚本、错误处理、陷阱、退出代码、清理例程、调试、健壮性,适合自动化任务和系统管理。

DevOps 0 次安装 0 次浏览 更新于 3/25/2026

名称: shell-error-handling 用户可调用: false 描述: 用于在实现Shell脚本中的错误处理、清理例程或调试时使用。涵盖陷阱、退出代码和健壮的错误模式。 允许的工具:

  • 读取
  • 写入
  • 编辑
  • Bash
  • Grep
  • Glob

Shell错误处理

在Shell脚本中健壮的错误处理、清理和调试的模式。

退出代码

标准退出代码

代码 含义
0 成功
1 一般错误
2 误用Shell命令
126 命令不可执行
127 命令未找到
128+N 致命信号N
130 Ctrl+C (SIGINT)

检查退出状态

# 检查最后一个命令的退出状态
if ! command; then
    echo "Command failed with status $?" >&2
    exit 1
fi

# 替代模式
command || {
    echo "Command failed" >&2
    exit 1
}

# 捕获退出状态
command
status=$?
if (( status != 0 )); then
    echo "Failed with status $status" >&2
fi

陷阱用于清理

基本清理模式

#!/usr/bin/env bash
set -euo pipefail

cleanup() {
    local exit_code=$?
    # 移除临时文件
    rm -f "$TEMP_FILE" 2>/dev/null || true
    exit "$exit_code"
}

trap cleanup EXIT

TEMP_FILE=$(mktemp)
# 脚本继续...
# cleanup在退出时自动运行

处理多个信号

#!/usr/bin/env bash
set -euo pipefail

cleanup() {
    echo "Cleaning up..." >&2
    rm -rf "$WORK_DIR" 2>/dev/null || true
}

handle_interrupt() {
    echo "Interrupted by user" >&2
    cleanup
    exit 130
}

trap cleanup EXIT
trap handle_interrupt INT TERM

WORK_DIR=$(mktemp -d)

陷阱最佳实践

# 在清理中保留原始退出代码
cleanup() {
    local exit_code=$?
    # 清理操作在这里
    rm -f "$temp_file" 2>/dev/null || true
    # 恢复退出代码
    exit "$exit_code"
}

# 使用 || true 用于可选的清理
trap 'rm -f "$temp_file" 2>/dev/null || true' EXIT

错误报告

标准错误输出

# 总是将错误写入stderr
echo "Error: Something went wrong" >&2

# 错误函数
error() {
    echo "Error: $*" >&2
}

# Die函数 - 错误并退出
die() {
    echo "Fatal: $*" >&2
    exit 1
}

# 使用
[[ -f "$config" ]] || die "Config file not found: $config"

详细日志记录

#!/usr/bin/env bash
set -euo pipefail

VERBOSE="${VERBOSE:-false}"

log() {
    if [[ "$VERBOSE" == "true" ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
    fi
}

error() {
    echo "[ERROR] $*" >&2
}

log "Starting script"
log "Processing file: $file"

防御性编程

检查先决条件

# 检查必需命令是否存在
require_command() {
    command -v "$1" >/dev/null 2>&1 || {
        echo "Error: Required command '$1' not found" >&2
        exit 1
    }
}

require_command jq
require_command curl
require_command shellcheck

验证输入

# 验证参数
if [[ $# -lt 2 ]]; then
    echo "Usage: $0 <source> <destination>" >&2
    exit 1
fi

source_file="$1"
dest_dir="$2"

# 验证文件存在
[[ -f "$source_file" ]] || {
    echo "Error: Source file not found: $source_file" >&2
    exit 1
}

# 验证目录
[[ -d "$dest_dir" ]] || {
    echo "Error: Destination directory not found: $dest_dir" >&2
    exit 1
}

安全临时文件

# 创建安全临时文件
TEMP_FILE=$(mktemp) || {
    echo "Error: Failed to create temp file" >&2
    exit 1
}

# 创建安全临时目录
TEMP_DIR=$(mktemp -d) || {
    echo "Error: Failed to create temp directory" >&2
    exit 1
}

# 总是清理
trap 'rm -rf "$TEMP_FILE" "$TEMP_DIR" 2>/dev/null || true' EXIT

调试

调试模式

#!/usr/bin/env bash

# 通过环境变量启用调试模式
if [[ "${DEBUG:-}" == "1" ]]; then
    set -x
fi

set -euo pipefail

# 或通过标志切换
while getopts "d" opt; do
    case $opt in
        d) set -x ;;
        *) echo "Usage: $0 [-d]" >&2; exit 1 ;;
    esac
done

跟踪执行

# 为特定部分启用跟踪
set -x
problematic_code
set +x

# 使用自定义PS4跟踪
export PS4='+ ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x

错误恢复模式

重试模式

retry() {
    local max_attempts="${1:-3}"
    local delay="${2:-1}"
    shift 2
    local cmd=("$@")

    local attempt=1
    while (( attempt <= max_attempts )); do
        if "${cmd[@]}"; then
            return 0
        fi
        echo "Attempt $attempt failed, retrying in ${delay}s..." >&2
        sleep "$delay"
        (( attempt++ ))
    done

    echo "All $max_attempts attempts failed" >&2
    return 1
}

# 使用
retry 3 5 curl -f "http://example.com/api"

回退模式

# 尝试主要方法,回退到次要方法
get_config() {
    if [[ -f "$HOME/.config/myapp/config" ]]; then
        cat "$HOME/.config/myapp/config"
    elif [[ -f "/etc/myapp/config" ]]; then
        cat "/etc/myapp/config"
    else
        echo "Error: No config file found" >&2
        return 1
    fi
}