
高级 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; }

参数解析
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

函数与返回值
# 通过全局变量或 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

数组与关联数组
# 索引数组
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 清理 |
不留临时文件 |