04 // 正文
Shell脚本自动化运维
运维人的终极梦想,是用一个脚本搞定所有重复劳动,然后腾出时间好好喝杯咖啡。备份、健康检查、磁盘清理——这些天天都要干、干起来又枯燥的操作,正是 Shell 脚本大显身手的地方。本文从脚本骨架讲起,给出三个可以直接落地的实战脚本,最后聊聊怎么写才"稳"、怎么写才"安全"。
Shell 脚本的骨架
Shell 脚本本质就是把一串命令按顺序装进一个文件,再给上执行权限。一个标准开头长这样:
#!/bin/bash
# 系统信息报告脚本
echo "系统信息报告 - $(date)"
echo "========================"
echo "当前用户: $(whoami)"
echo "主机名: $(hostname)"
echo "系统版本: $(grep PRETTY_NAME /etc/os-release | cut -d'\"' -f2)"
第一行 #!/bin/bash(Shebang)告诉系统用哪个解释器来跑这个脚本。写完后记得:
chmod +x report.sh # 赋予执行权限
./report.sh # 运行
💡 小知识
脚本里尽量写绝对路径(如 /usr/bin/tar),或者至少在脚本开头 export PATH。否则定时任务(crontab)环境里 PATH 极简,很容易出现"手动跑正常、定时跑报错"的诡异问题。
实战一:自动备份脚本
备份是运维的底线,但天天手动打包太傻。这个脚本会压缩指定目录、加上时间戳,并自动清理 7 天前的旧备份:
#!/bin/bash
# 数据备份脚本
BACKUP_DIR="/backup"
SOURCE_DIR="/data"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# 打包压缩源目录
tar -czf "$BACKUP_DIR/backup_$DATE.tar.gz" "$SOURCE_DIR"
# 删除 7 天前的备份
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
echo "备份完成: backup_$DATE.tar.gz"
配合 crontab -e 加一行 0 2 * * * /opt/scripts/backup.sh,每天凌晨自动执行。想更"现代"一点,也可以交给 systemd timer 来调度。
实战二:服务自愈脚本
服务挂了不会自动爬起来是常态,那就写个"护工":检测服务状态,挂了就尝试重启,重启失败再发邮件告警。
#!/bin/bash
# 服务监控与自愈脚本
SERVICE="nginx"
LOG_FILE="/var/log/monitor.log"
if systemctl is-active --quiet "$SERVICE"; then
echo "$(date): $SERVICE 运行正常" >> "$LOG_FILE"
exit 0
fi
echo "$(date): $SERVICE 不在运行,尝试重启..." >> "$LOG_FILE"
systemctl restart "$SERVICE"
if systemctl is-active --quiet "$SERVICE"; then
echo "$(date): $SERVICE 重启成功" >> "$LOG_FILE"
else
echo "$(date): $SERVICE 重启失败,发送告警" >> "$LOG_FILE"
echo "Service $SERVICE failed to restart" | mail -s "Critical: Service Down" admin@example.com
fi
⚠️ 提醒:自愈脚本要设"重启上限",否则服务因配置错误反复拉起会刷爆日志,甚至造成雪崩。建议记录连续失败次数,超过阈值就停止并告警,等人来排查。
实战三:磁盘清理脚本
磁盘满了是最常见的事故之一。这个脚本清扫临时目录、删掉 30 天前的日志,还系统一片净土:
#!/bin/bash
# 临时文件与日志清理脚本
LOG_FILE="/var/log/cleanup.log"
echo "$(date): 开始清理临时文件" >> "$LOG_FILE"
# 删除临时目录里 7 天前的文件
for dir in /tmp /var/tmp; do
find "$dir" -type f -mtime +7 -delete
find "$dir" -type d -empty -delete
done
# 清理 30 天前的日志文件
find /var/log -name "*.log" -mtime +30 -delete
echo "$(date): 清理完成" >> "$LOG_FILE"
磁盘告警 + 这个脚本 + 定时任务,基本能杜绝"磁盘悄悄满了"的惨案。
写脚本的三条"稳"字诀
- 开启严格模式:脚本开头加
set -euo pipefail,出错就停、变量未定义就报、管道中间断了也不放过 - 变量加引号:
"$VAR"永远比$VAR稳,路径带空格时不会翻车 - 关键步骤留日志:每一步干了什么写到日志里,出问题时才有迹可循
#!/bin/bash
set -euo pipefail
LOG_FILE="/var/log/cleanup.log"
log() { echo "$(date +'%F %T') $*" >> "$LOG_FILE"; }
log "开始清理"
find "/tmp" -type f -mtime +7 -delete
log "清理完成"
📌 注意:脚本里的敏感信息(密码、Token)不要硬编码。从环境变量或配置文件读取,避免脚本泄露到 Git 仓库或日志里。
安全注意事项
- 不要硬编码密码,用环境变量或
.env文件,并设置600权限 - 对用户输入和参数做校验,防止注入
- 命令写绝对路径,避免 PATH 被污染后执行到假命令
- 脚本文件权限收紧,防止被未授权修改
- 定期审查脚本,删掉不再需要的功能
最佳实践建议
- 先测后上:脚本先在测试机跑几遍,再进生产定时任务
- 幂等优先:同样的脚本跑两次,结果应该一致,不会出乱子
- 输出带时间戳:日志里的每条记录都带时间,回溯才有意义
- 留一个"手动开关":关键脚本支持
--dry-run,上线前先演练一遍 - 脚本入库:把脚本放进 Git 仓库,改了什么一目了然
自动化不是"让机器代替人",而是把人的精力从重复劳动中解放出来,去解决机器解决不了的问题。脚本写得越稳,半夜被电话吵醒的概率就越低。
从备份、自愈到清理,这三个脚本基本覆盖了运维日常最典型的重复操作。跑通之后,可以再学 systemd timer 替代 crontab、学 Ansible 做批量分发,一步步把"手工人肉"升级成"机器托管"。