深色模式
运维脚本总览
摘要:脚本是运维把重复劳动变成可复用资产的主要方式。本文先讲清「什么情况下值得写脚本、什么情况下不该写」,再给出一套通用的脚本工程骨架——严格模式、日志、幂等、错误处理,让脚本不至于变成新的事故来源。
适用环境
bash
# 确认 Bash 与 Python 版本
bash --version | head -1
python3 --version
which shellcheck || echo "建议安装 shellcheck"1
2
3
4
2
3
4
操作步骤
一、先判断:该不该写脚本
| 场景 | 建议 |
|---|---|
| 重复执行 3 次以上 | 写脚本 |
| 需要在多台机器上做同一件事 | 用脚本 + 批量执行(或配置管理工具) |
| 需要定时、无人值守 | 写脚本 |
| 只做一次的一次性变更 | 手工做,写文档记录即可 |
| 涉及复杂状态收敛(装软件、管配置、保证幂等) | 用 Ansible / Salt / 配置管理,不要硬写 Shell |
注意
脚本的价值在于「可重复」,而不是「自动化」本身。为了一次性的操作写脚本,往往换来一个没人维护、没人敢跑的半成品。
二、脚本骨架:六件事
每个脚本都应包含这六项,缺一项就可能在半夜出问题:
bash
#!/usr/bin/env bash
# 1) 严格模式:出错即退出、未定义变量报错、管道错误可捕获
set -euo pipefail
# 2) 常量与默认参数放顶部,方便改
readonly LOG_DIR="/var/log/ops"
readonly RETENTION_DAYS=7
# 3) 日志函数:统一格式与时间戳
log() {
printf '[%s] [%s] %s\n' "$(date '+%F %T')" "${1:-INFO}" "${2:-}" >&2
}
# 4) 清理钩子:无论成功失败都执行
cleanup() {
local rc=$?
log INFO "exit=$rc"
rm -f "${TMP_FILE:-}" 2>/dev/null || true
}
trap cleanup EXIT
# 5) 参数校验
if [[ $# -lt 1 ]]; then
log ERROR "用法: $0 <参数>"
exit 2
fi
# 6) 主逻辑
log INFO "start: $*"
mkdir -p "$LOG_DIR"
log INFO "done"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
三、幂等:能重复跑而不出错
幂等是脚本能不能放心交给 crontab 的关键。常见写法:
bash
# 不幂等:第二次执行报错
useradd deployer
# 幂等:先判断再执行
id deployer &>/dev/null || useradd deployer
# 写入配置前判断内容是否已存在
grep -qxF "deployer ALL=(ALL) NOPASSWD:ALL" /etc/sudoers.d/deployer 2>/dev/null || \
echo "deployer ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/deployer1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
四、错误处理:不要静默失败
bash
# 反例:失败没人知道
rm -rf /data/old || true
# 正例:捕获、记录、退出非零
if ! rm -rf /data/old; then
log ERROR "清理失败,中止后续操作"
exit 1
fi1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
危险
不要用 || true 掩盖所有错误。真正的失败被吞掉后,脚本会带着错误状态继续往下跑,造成的破坏往往比直接失败更大。只在「确实可以忽略」的地方使用,并写注释说明原因。
五、安全默认值
bash
# 创建临时文件用 mktemp,避免固定路径被竞态利用
TMP_FILE="$(mktemp /tmp/ops.XXXXXX)"
# 变量一律加引号,防止含空格时参数被拆开
cp "$src" "$dst"
# 危险操作前打印将要影响的路径
log INFO "will delete: $target"1
2
3
4
5
6
2
3
4
5
6
六、脚本也要有版本与评审
bash
# 脚本进 Git,便于回溯与回滚
git init ops-scripts && cd ops-scripts
git add backup.sh && git commit -m "add backup script"
# 上线前做静态检查
shellcheck backup.sh
# 先在测试环境跑,再上生产1
2
3
4
5
6
2
3
4
5
6
验证
- [ ] 脚本带
#!/usr/bin/env bash与set -euo pipefail - [ ]
shellcheck无 error 级告警 - [ ] 连续执行两次结果一致(幂等)
- [ ] 故意让中间一步失败,脚本能非零退出并输出可读错误
- [ ] 脚本已进 Git,有提交信息
常见坑
- 不加
set -e:中间命令失败了脚本还继续跑,把错误结果写进生产。 - 变量不加引号:路径含空格时被拆成两个参数,轻则报错重则误删。
- 用固定临时文件名:
/tmp/xxx.tmp会被并发或恶意抢占,必须用mktemp。 - 在脚本里写死绝对路径和机器名:换个环境就跑不了,应使用参数或配置文件。
- 脚本跑完没日志:出事之后无从查证,至少要记录开始、结束、关键步骤。