操作系统学习笔记 · 第 28 课 · Shell 实战——自动化运维脚本
前几课,你学了一整套 shell 武器:变量、判断、循环、函数、管道、三剑客。但光会写还不够——一个能上生产的脚本,还得健壮(出错能处理)、能定时自动跑(cron)、好排错。这一课,我们把知识合起来,写一个真正能用的自动化运维脚本,并让它按时自动执行。这是第七阶段(Shell 与脚本编程)的收官课。
从"人工运维"到"自动运维"
想象你每天要做这些事:备份文件、检查磁盘和内存、清理旧备份、记日志。天天手动敲一遍,又累又容易忘。
自动化运维脚本就是解法:把这些事写成一个脚本,再用 cron 定时执行——到点它自己跑,你睡大觉。这一课,我们把这个流程完整走一遍。
28.1 综合脚本的结构
一句话理解:一个合格的运维脚本 = 头部(#!/bin/bash+set -euo pipefail)+ 配置区(路径/阈值)+ 核心逻辑 + 日志输出 + 退出码。
一个合格脚本,结构清晰、分块:
#!/bin/bash # 1. 头部:指定解释器
set -euo pipefail # 健壮开头
# ---- 配置区 ----
SRC="$HOME/Documents" # 2. 配置:路径、阈值等,集中放前面,好改
BACKUP_DIR="$HOME/backups"
# ---- 核心逻辑 ---- # 3. 真正干活的地方
...
# ---- 日志输出 ---- # 4. 记录做了什么
...
exit 0 # 5. 退出码为什么配置要集中放前面?因为路径、阈值这些"以后会改"的东西,集中起来一眼就能改,不用满脚本找。
28.2 cron 定时任务
一句话理解:cron 是"定时器",crontab -e编辑,格式是分 时 日 月 周 命令,到点自动跑。
写好了脚本,怎么让它"每天自动跑"?靠 cron。
编辑定时任务:crontab -e,加一行:
分 时 日 月 周 命令五个时间字段,从"分"到"周":
| 字段 | 范围 | 含义 |
|---|---|---|
| 分 | 0-59 | 第几分钟 |
| 时 | 0-23 | 第几小时 |
| 日 | 1-31 | 几号 |
| 月 | 1-12 | 几月 |
| 周 | 0-7(0 和 7 都是周日) | 星期几 |
例子:
0 2 * * * /home/user/backup.sh:每天凌晨 2 点跑备份(*表示"任意");*/5 * * * * /path/check.sh:每 5 分钟一次(*/5表示"每隔 5");0 3 * * 1 /path/weekly.sh:每周一凌晨 3 点。
28.3 健壮性技巧
一句话理解:trap '清理动作' EXIT让脚本退出前必做清理;set -u防未定义变量;logger把日志写进系统日志。
三个让脚本更皮实的技巧:
① trap——退出前必做清理:
trap 'echo "备份中断" >> "$LOG"' ERR # 出错时记录
trap 'rm -f /tmp/tmpfile' EXIT # 无论成功失败,退出前删临时文件trap 监听某个"信号"或"退出"事件,触发时执行指定动作。这样脚本中途出错,也能清理临时文件、不留垃圾。② set -u:上一课讲过,用未定义变量就报错,防拼写错误。
③ logger:把日志写进系统日志(而不是只写自己的文件):
echo "⚠️ 磁盘使用 95%" | logger -t monitorlogger -t monitor给这条日志打上monitor标签,写进系统日志,方便集中监控。
28.4 排错思路
脚本出问题了,按这个顺序排查:
一句话理解:先 bash -x script.sh 逐条看执行到哪出错,再看退出码和 stderr,最后检查权限、路径、环境变量。bash -x script.sh:逐条打印每条命令(前面带+),看执行到哪一步出错;- 看退出码和 stderr:
echo $?看退出码,stderr 的错误信息往往直接指出原因; - 检查权限、路径、环境变量:文件有可执行权限吗?路径对了吗?
cron 报错的经典坑:cron 运行环境不读~/.bashrc,PATH极简。你在终端能跑的脚本,cron 里可能找不到命令。所以 cron 里的脚本要用绝对路径,或显式设置PATH。
动手实验:备份 + 监控脚本
备份脚本:
#!/bin/bash
set -euo pipefail
# ---- 配置区 ----
SRC="$HOME/Documents"
BACKUP_DIR="$HOME/backups"
KEEP=7
LOG="$BACKUP_DIR/backup.log"
mkdir -p "$BACKUP_DIR"
trap 'echo "备份中断" >> "$LOG"' ERR
# ---- 备份 ----
stamp=$(date +%Y%m%d_%H%M%S)
tar -czf "$BACKUP_DIR/doc_$stamp.tar.gz" -C "$SRC" . >> "$LOG" 2>&1
echo "[$stamp] 备份完成" >> "$LOG"
# ---- 清理旧备份,只保留最近 KEEP 份 ----
ls -1t "$BACKUP_DIR"/doc_*.tar.gz | tail -n +$((KEEP+1)) | xargs -r rm -f监控脚本(磁盘 + 内存水位):
#!/bin/bash
set -uo pipefail
DISK_TH=90
MEM_TH=90
disk=$(df / | awk 'NR==2 {gsub("%","",$5); print $5}')
mem=$(free | awk 'NR==2 {printf "%.0f", $3/$2*100}')
[ "$disk" -gt "$DISK_TH" ] && echo "⚠️ 磁盘使用 ${disk}%" | logger -t monitor
[ "$mem" -gt "$MEM_TH" ] && echo "⚠️ 内存使用 ${mem}%" | logger -t monitor配 cron:
crontab -e
# 加一行:
0 3 * * * /home/user/backup.sh明天凌晨 3 点自动备份。观察 $LOG 和 logger 写入的系统日志。
读一遍监控脚本:df /取磁盘,awk 'NR==2'取第 2 行、gsub("%","",$5)去掉第 5 列的百分号——得到磁盘使用率数字;free同理算出内存使用率;超过阈值就logger告警。每一行都是前几课的知识拼起来的。
深入点:两个进阶话题
① cron 的环境陷阱
cron 不读~/.bashrc,PATH极简。所以脚本里最好写绝对路径(如/usr/bin/tar而不是tar),或显式export PATH=...。
② 锁文件防重入
定时任务可能还没跑完又被触发(比如每 5 分钟一次,但某次跑了 10 分钟)。防止并发执行:
flock -n /tmp/backup.lock ./backup.shflock -n:拿到锁才执行,拿不到(说明上次还没跑完)就直接退出,避免两个实例同时跑、互相打架。小结与思考题
这一课,我们把知识合成了一个真正的运维脚本:
- 脚本结构:头部 + 配置区 + 核心逻辑 + 日志 + 退出码;
- cron:
分 时 日 月 周 命令,定时自动跑; - 健壮性:
trap清理、set -u、logger写系统日志; - 排错:
bash -x逐条看 → 退出码/stderr → 权限/路径/环境。
留三个问题:
- 一个健壮脚本的标准开头是什么?
- cron 的时间格式怎么读?
- 为什么 cron 里的脚本常要用绝对路径?
到这里,第七阶段(Shell 与脚本编程,25~28 课)收官。前面我们一直在"一台机器"里打转。但现代系统,几乎都是跑在云上、容器里的。你可能天天用 Docker,却未必想过:Docker 到底借用了 Linux 内核的哪些能力,才做到"秒级启动、互相隔离"? 它和虚拟机有什么本质区别?下一课,我们进入第八阶段:虚拟化与容器,把 Docker 的"黑魔法"拆开看。