Shell 脚本实战:自动化运维的必备利器
在系统管理员的日常里,Shell 脚本是连接"手工操作"与"自动化平台"之间的桥梁。一个写得规范的脚本,能安全地完成批量部署、定时巡检、日志清理等重复劳动。本文从语法根基到生产实践,系统讲解 Shell 脚本的编写之道。
一、脚本的骨架:规范先行
#!/usr/bin/env bash
# 标题:日志清理脚本
# 作者:ops
# 日期:2026-10-20
set -euo pipefail # 三条保命指令,见下文
LOG_DIR="/var/log/app"
RETENTION_DAYS=30
main() {
if [[ ! -d "$LOG_DIR" ]]; then
echo "错误: 日志目录不存在: $LOG_DIR" >&2
exit 1
fi
find "$LOG_DIR" -type f -name "*.log" -mtime +"$RETENTION_DAYS" -delete
echo "清理完成: 删除 $(find "$LOG_DIR" -type f -name "*.log" -mtime +"$RETENTION_DAYS" | wc -l) 个文件"
}
main "$@"
1.1 set -euo pipefail 的含义
set -e:任何命令失败立即退出,避免"失败后继续执行"的连锁错误;set -u:使用未定义变量即报错,揪出拼写错误;set -o pipefail:管道中任一环节失败,整条管道返回失败——否则cmd | grep只认 grep 的退出码。
这三条是生产脚本的底线。
二、变量、数组与引号的艺术
2.1 变量与默认值
env="${1:-production}" # 参数缺省值
user="${USER:-unknown}" # 环境变量缺省
readonly APP_PORT=8080 # 只读变量,防误改
2.2 数组与遍历
servers=("web-01" "web-02" "web-03")
for s in "${servers[@]}"; do
ssh "$s" "systemctl status nginx" >/dev/null 2>&1 \
&& echo "$s OK" || echo "$s FAIL"
done
2.3 永远给变量加引号
# 反例:路径含空格时直接炸裂
rm -rf $path
# 正例
rm -rf "$path"
引号原则:所有变量展开都要加双引号,除非你明确需要分词(罕见)。
三、条件判断与函数
3.1 文件与数值判断
if [[ -f "$conf" ]] && [[ -r "$conf" ]]; then # 文件存在且可读
echo "配置可读"
fi
if (( disk_usage > 90 )); then # 数值比较用 (())
echo "磁盘告警"
fi
[[ ]] 中的 -f/-d/-x/-z 分别对应"是文件/目录/可执行/为空",配合 &&、|| 组合成业务判断。
3.2 函数组织
log() { echo "[$(date '+%F %T')] $*" >> "$LOG_FILE"; }
die() { log "FATAL: $*"; exit 1; }
脚本超过 50 行务必用函数分层:主函数 main 在最底部,被调用前定义。
四、实战:部署脚本的标准结构
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="api"
RELEASE_DIR="/opt/$APP_NAME"
BACKUP_DIR="/var/backups/$APP_NAME"
backup() {
local ts
ts=$(date '+%Y%m%d%H%M%S')
cp -a "$RELEASE_DIR" "$BACKUP_DIR/$APP_NAME-$ts"
log "已备份到 $BACKUP_DIR/$APP_NAME-$ts"
}
deploy() {
local pkg="$1"
backup
systemctl stop "$APP_NAME"
tar -xzf "$pkg" -C "$RELEASE_DIR"
systemctl start "$APP_NAME"
sleep 3
if systemctl is-active --quiet "$APP_NAME"; then
log "部署成功"
else
log "启动失败,尝试回滚"
rollback
fi
}
main() {
[[ $# -eq 1 ]] || die "用法: $0 <包路径>"
deploy "$1"
}
main "$@"
这套结构包含了生产部署的必备要素:备份、幂等步骤、健康检查、失败回滚。
五、定时任务与日志
5.1 crontab 最佳实践
# 分 时 日 月 周
30 2 * * * /opt/scripts/backup_db.sh >> /var/log/cron/backup_db.log 2>&1
要点:cron 环境 PATH 极简,脚本内必须写全路径;输出重定向到专属日志,否则排错无从下手;关键任务加重试与锁。
5.2 防止重复执行:flock
exec 9>"$LOCK_FILE"
flock -n 9 || { echo "已有实例在运行"; exit 1; }
六、十个常见坑
- CRLF 换行符:Windows 编辑的脚本报
\r错误,用sed -i 's/\r$//'清洗; if [ $x == 1 ]单中括号:空变量直接报错,统一用[[ ]];$?判断太晚:两条命令之间夹了其他命令,$?已失效,立即判断;cp覆盖符号链接:cp默认跟随链接,注意-P选项;- find + rm 与空格:
find ... | xargs rm遇空格炸裂,改用find ... -delete; - 超时缺失:
ssh、curl卡死拖住整个脚本,一律加timeout; - 并发无锁:cron 与手动执行撞车,用 flock 上锁;
- 日志不落盘:脚本输出到终端被 cron 丢弃,必须重定向;
- 变量未引号:空格/通配符导致意外行为,见 2.3;
set -e与grep配合:grep无匹配返回 1 触发退出,用|| true或if grep -q。
结语
Shell 脚本的价值在于把运维经验固化成可重复的执行路径。规范的三条保命指令、严谨的引号习惯、清晰的主函数结构、完备的备份回滚——这些纪律让脚本从"能跑"进化到"可靠"。当你的脚本库沉淀了部署、巡检、备份、清理四大类标准件,你就拥有了把几百台机器从"手忙脚乱"变成"按部就班"的能力。
评论(0)
登录 后参与评论。