教程
Shell 脚本自动化运维入门:从一条命令到定时任务
为什么要把命令写成脚本
AI 操作示意图·以官网实际界面为准
手动在服务器上敲一遍"打包网站目录、导出数据库、传到备份盘",三分钟搞定;可一旦每周都得做一遍,就开始错漏了——忘了导数据库、传错目录、断点没续传。把这些命令攒进一个 .sh 文件,就是脚本。它的好处不是"高级",而是同样的步骤每天都能一模一样地跑。
1. 一个脚本的骨架
#!/bin/bash
set -euo pipefail
WEB_DIR="/var/www/example.com"
BACKUP_DIR="/data/backups"
echo "[1/3] 打包网站目录"
tar -czf "$BACKUP_DIR/site_$(date +%F).tar.gz" "$WEB_DIR"
echo "[2/3] 备份完成,列一下结果"
ls -lh "$BACKUP_DIR"
几个点说一下:
- 首行
#!/bin/bash告诉系统用哪个解释器跑这个文件,省得依赖当前登录 shell。 - 变量赋值等号两边不能有空格:
WEB_DIR="/var/www"对,WEB_DIR = "/var/www"会被当成命令调用。 - 变量取值要加
$,而且带空格的路径一定要加双引号,否则空格会把一个参数劈成两个。
2. set -euo pipefail 是什么意思
AI 操作示意图·以官网实际界面为准
这一行是我写脚本一定会加的"防呆三件套",Bash 手册里都有明确说明:
| 选项 | 作用 | 不加会怎样 |
|---|---|---|
set -e |
命令返回非零就立刻退出,不往下硬跑 | 前一步失败了后面还接着跑,错上加错 |
set -u |
用到没定义的变量直接报错 | $BACKUP 打错成 $BAKCUP 默默变成空,路径清空 |
set -o pipefail |
管道里任何一环失败都算失败 | cmd1 | cmd2 里 cmd1 崩了却被当成成功 |
不过 set -e 有个坑:手册里写明,放在 if、&&、|| 后面的命令失败不会触发退出。所以像 grep 这种"找不到很正常"的命令,要么写成 grep ... || true,要么单独判断,别让它把脚本误杀。
3. 条件、循环、函数
# 条件
if [ -d "$BACKUP_DIR" ]; then
echo "备份目录存在"
else
mkdir -p "$BACKUP_DIR"
fi
# 循环:处理目录里每个文件
for f in "$WEB_DIR"/*.log; do
echo "清理 $f"
rm -f "$f"
done
# 函数
backup_one() {
local name="$1"
tar -czf "$BACKUP_DIR/${name}_$(date +%F).tar.gz" "/var/www/$name"
}
backup_one "blog"
函数里的局部变量用 local 声明(Bash 手册:local 只能在函数内用),避免和外面的全局变量撞名。$1 是函数收到的第一个参数。
4. 交给 cron 定时跑
写好的脚本先 chmod +x backup.sh,再让 cron 定时执行。编辑 crontab:
crontab -e
加一行(每天凌晨 2 点跑一次):
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
>> /var/log/backup.log 2>&1 这句是把标准输出和错误都追加到日志,不然脚本静默失败你完全不知道。cron 环境变量很干净,脚本里一律用绝对路径,别依赖你登录时的 PATH。

我怎么知道脚本写得对不对?
装个 ShellCheck(GitHub: koalaman/shellcheck),它是个专门检查 shell 脚本的静态分析工具。本地跑 shellcheck backup.sh,它会把没加引号的变量、错误的条件判断、危险的 rm -rf 全挑出来。也可以直接把脚本贴到它的在线版上看结果。
5. 一份最小可用的备份脚本
把前面的拼起来,就是一个能直接用的备份骨架:
#!/bin/bash
set -euo pipefail
WEB_DIR="/var/www/example.com"
DB_NAME="exampledb"
BACKUP_DIR="/data/backups"
mkdir -p "$BACKUP_DIR"
STAMP=$(date +%F)
echo "[$STAMP] 开始备份"
tar -czf "$BACKUP_DIR/site_$STAMP.tar.gz" "$WEB_DIR"
mysqldump "$DB_NAME" | gzip > "$BACKUP_DIR/db_$STAMP.sql.gz"
find "$BACKUP_DIR" -mtime +14 -delete # 删除 14 天前的旧备份
echo "[$STAMP] 备份完成"
find ... -mtime +14 -delete 这行删 14 天前的文件,第一次跑先把 -delete 去掉,确认 find 列出来的就是你想删的,再加回来。
官方资料
资料核对日期:2026-10-25
- GNU Bash 官方手册(变量 / 函数 / set -euo pipefail / local):https://www.gnu.org/software/bash/manual/bash.html
- ShellCheck GitHub 仓库(shell 脚本静态检查工具):https://github.com/koalaman/shellcheck
最近一周热门文章
按近 7 天访问热度排序,热度积累前优先展示新文章。
当前栏目最新文章
教程 · 继续阅读同栏目的新内容
一起交流
0 条留言LEAVE A NOTE
你的经验,也很重要。
无需注册或登录。请勿公开邮箱密码、证件、银行卡号或其他敏感信息。
















还没有留言,来分享你的想法吧。