1. 无条件开启 set -euo pipefail

这是新手脚本和可靠脚本最大的分水岭。三行选项各干一件事:

#!/usr/bin/env bash
set -euo pipefail
例外 某些命令「预期会失败」时(如检查进程是否存在的 pgrep), 用 command || trueif command; then ... fi 显式处理, 而不是关掉 -e。

2. 参数校验与默认值

脚本要有「脾气」——参数不对就拒绝执行:

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

APP_ENV="${1:?用法: $0 }"
REGION="${REGION:-cn-north-1}"        # 环境变量缺省值
REPLICAS="${REPLICAS:-3}"

case "$APP_ENV" in
  dev|prod) ;;
  *) echo "非法环境: $APP_ENV" >&2; exit 1 ;;
esac

3. 统一的日志函数

输出带时间戳的日志,且把信息与错误分流到 stdout/stderr:

log()  { echo "[$(date '+%F %T')] $*"; }
info() { log "[INFO] $*"; }
warn() { log "[WARN] $*" >&2; }
err()  { log "[ERROR] $*" >&2; exit 1; }

info "开始部署 $APP_ENV"
warn "磁盘使用率偏高,请关注"
err "配置文件缺失"

好处立竿见影:脚本被 cron 或 CI 收集日志时,时间线一目了然;>&2 保证错误不会被误当正常输出重定向掉。

4. 并行执行与等待

运维里最常见的提速场景:多台机器同时执行同一任务。用 &wait

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

HOSTS=(10.0.0.1 10.0.0.2 10.0.0.3)
PIDS=()

for h in "${HOSTS[@]}"; do
  ( ssh "$h" 'systemctl restart app' && echo "[OK] $h" ) &
  PIDS+=("$!")
done

# 逐个检查失败结果,避免 set -e 提前终止整个脚本
for pid in "${PIDS[@]}"; do
  wait "$pid" || { echo "有主机执行失败 (pid=$pid)" >&2; FAILED=1; }
done
exit "${FAILED:-0}"
为什么每个子 shell 里加 set -e 的行为不同 子 shell 中失败会导致整个脚本提前退出,因此把「单个主机的成败」封装进子 shell, 用 wait 精确收集每个任务的退出码——这正是并行脚本的推荐写法。

5. 收尾:脚本框架模板

把以上技巧拼成一个可直接复用的骨架:

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

log()  { echo "[$(date '+%F %T')] $*"; }
info() { log "[INFO] $*"; }
warn() { log "[WARN] $*" >&2; }
err()  { log "[ERROR] $*" >&2; exit 1; }

APP_ENV="${1:?用法: $0 }"

cleanup() { info "清理临时文件"; }
trap cleanup EXIT

info "开始执行,环境: $APP_ENV"
# ... 业务逻辑 ...
info "执行完成"

最后提醒一条:脚本写好后一定跑一遍 bash -n script.sh 做语法检查, 再用 shellcheck 做静态分析——它能抓出大部分新手甚至老手都会犯的错误。好脚本和坏脚本的差别, 往往就在这些「微不足道」的细节里。

← 返回首页