正在加载,请稍候…

高级 Bash 脚本:函数、错误处理与自动化

编写专业的 Bash 自动化脚本,涵盖函数、错误处理、参数解析、日志记录、并行执行及常见脚本模式。

高级 Bash 脚本:函数、错误处理与自动化

高级 Bash 脚本

脚本模板

#!/usr/bin/env bash
# Script: deploy.sh
# Description: Automated deployment script
# Usage: ./deploy.sh [options] <environment>

set -euo pipefail  # 遇到错误、未定义变量、管道失败时退出
IFS=
#39;\n\t' # 更安全的分词 # 脚本目录 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR # 默认值 LOG_FILE="${SCRIPT_DIR}/deploy.log" DRY_RUN=false ENVIRONMENT="" # 颜色 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # 日志 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; } info() { log "${GREEN}[INFO]${NC} $*"; } warn() { log "${YELLOW}[WARN]${NC} $*"; } error() { log "${RED}[ERROR]${NC} $*" >&2; } die() { error "$@"; exit 1; }

高级 Bash 脚本:函数、错误处理与自动化 插图

参数解析

usage() {
  cat << EOF
Usage: $(basename "$0") [options] <environment>

Options:
  -h, --help     Show help
  -n, --dry-run  Dry run mode
  -v, --verbose  Verbose output
  -t, --tag TAG  Docker image tag

Environments:
  staging, production
EOF
  exit 0
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      -h|--help)    usage ;;
      -n|--dry-run) DRY_RUN=true ;;
      -v|--verbose) set -x ;;
      -t|--tag)
        [[ -n "${2:-}" ]] || die "Option $1 requires an argument"
        TAG="$2"
        shift 2
        ;;
      --)  shift; break ;;
      -*)  die "Unknown option: $1" ;;
      *)   ENVIRONMENT="$1"; shift ;;
    esac
  done

  [[ -n "$ENVIRONMENT" ]] || die "Environment required"
  [[ "$ENVIRONMENT" =~ ^(staging|production)$ ]] || die "Invalid environment: $ENVIRONMENT"
}

parse_args "$@"

错误处理与清理

# 退出时清理的陷阱
cleanup() {
  local exit_code=$?
  info "Cleaning up..."
  rm -f /tmp/deploy-$-*
  if [[ $exit_code -ne 0 ]]; then
    error "Script failed with exit code $exit_code"
  fi
  exit $exit_code
}
trap cleanup EXIT INT TERM

# 重试函数
retry() {
  local max=$1
  local delay=$2
  local cmd="${@:3}"
  local count=0

  until $cmd; do
    count=$((count + 1))
    if [[ $count -ge $max ]]; then
      error "Command failed after $max attempts: $cmd"
      return 1
    fi
    warn "Attempt $count failed. Retrying in ${delay}s..."
    sleep "$delay"
  done
}

retry 3 5 curl -sf https://api.example.com/health

高级 Bash 脚本:函数、错误处理与自动化 插图

函数与返回值

# 通过全局变量或 stdout 返回多个值
get_version() {
  local env="$1"
  local version
  version=$(git describe --tags --abbrev=0)
  echo "$version"  # 通过 stdout 返回
}

check_service() {
  local url="$1"
  local timeout="${2:-5}"
  curl --silent --fail --max-time "$timeout" "$url" > /dev/null 2>&1
  return $?  # 通过退出码返回
}

# 使用
VERSION=$(get_version "$ENVIRONMENT")
if check_service "https://api.example.com/health"; then
  info "Service is healthy"
else
  die "Service health check failed"
fi

并行执行

# 并行运行任务
deploy_services() {
  local pids=()

  for service in api worker scheduler; do
    (
      info "Deploying $service..."
      kubectl rollout restart deployment/$service -n production
      kubectl rollout status deployment/$service -n production --timeout=300s
    ) &
    pids+=($!)
  done

  # 等待所有并检查状态
  local failed=0
  for pid in "${pids[@]}"; do
    if ! wait "$pid"; then
      failed=$((failed + 1))
    fi
  done

  return $failed
}

# GNU Parallel 用于更精细的控制
parallel --jobs 4 --halt now,fail=1 \
  'echo "Processing {}" && process_item {}' \
  ::: item1 item2 item3 item4

高级 Bash 脚本:函数、错误处理与自动化 插图

数组与关联数组

# 索引数组
services=("api" "worker" "scheduler" "cron")

# 循环
for service in "${services[@]}"; do
  echo "Service: $service"
done

# 切片
echo "${services[@]:1:2}"  # worker scheduler

# 关联数组
declare -A config
config[host]="db.example.com"
config[port]="5432"
config[db]="myapp"

# 遍历键
for key in "${!config[@]}"; do
  echo "$key=${config[$key]}"
done

文本处理

# 字符串操作
name="hello-world"
echo "${name^^}"           # HELLO-WORLD(大写)
echo "${name^}"            # Hello-world(首字母大写)
echo "${name//-/_}"        # hello_world(替换)
echo "${name#hello-}"      # world(删除前缀)
echo "${name%%-*}"         # hello(删除后缀)
echo "${name:6:5}"         # world(子串)

# 进程替换
diff <(sort file1.txt) <(sort file2.txt)

# Here 文档
read -r -d '' SQL << 'EOF'
SELECT id, name, email
FROM users
WHERE created_at > NOW() - INTERVAL '7 days'
EOF

# 高效文本处理
grep -r "TODO" --include="*.ts" --exclude-dir=node_modules | wc -l
find . -name "*.log" -mtime +30 -exec rm {} \;

脚本最佳实践

实践 原因
set -euo pipefail 遇到错误快速失败
shellcheck 静态分析
变量加引号 防止分词
常量用 readonly 防止意外修改
带时间戳的日志 便于调试
使用 trap 清理 不留临时文件