为出海而探索,为选择多想一步。CROSSBORDER GEEK / K8N.COM
K8N跨境极客CrossBorder Geek

教程

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

最近一周热门文章

按近 7 天访问热度排序,热度积累前优先展示新文章。

当前栏目最新文章

教程 · 继续阅读同栏目的新内容

一起交流

0 条留言

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

LEAVE A NOTE

你的经验,也很重要。

无需注册或登录。请勿公开邮箱密码、证件、银行卡号或其他敏感信息。

更多阅读
K8N · CrossBorder Geek

给出海玩家、独立开发者、跨境博主与外贸人的选型笔记。

关于这个博客 ↗