作为个人站长,你可能经常遇到这样的重复劳动:每周手动备份一次网站文件、每天清理一次 Nginx 日志、半夜爬起来看网站是不是又挂了。这些事做一次两次不觉得,做上半年就会发现又烦又容易忘。Shell 脚本就是解决这类问题的钥匙:把重复的操作写成一段脚本,交给 crontab 定时执行,从此一劳永逸。这篇文章面向完全没有脚本基础的个人站长,从第一个 Hello 脚本讲起,穿插变量、条件、循环的基础知识,再带出备份、日志清理、健康检查三个拿来就能用的实战脚本,最后讲清楚 crontab 调度和排错技巧。
环境准备与第一个脚本
先确认环境。绝大多数 Linux 服务器自带 bash,在终端输入 echo $SHELL 应该能看到 /bin/bash。如果没有,用包管理器安装:Debian/Ubuntu 系执行 apt install bash,CentOS 系执行 yum install bash。然后新建一个脚本文件,比如 backup.sh:
#!/bin/bash
echo "Hello, 站长!"
echo "当前时间: $(date '+%Y-%m-%d %H:%M:%S')"第一行 #!/bin/bash 叫 shebang,告诉系统用哪个解释器执行这个文件,必须写在第一行。写完保存后,给脚本加上执行权限并运行:
chmod +x backup.sh
./backup.sh看到两行输出,你的第一个脚本就跑起来了。注意运行方式是 ./backup.sh 而不是 backup.sh,因为 Linux 默认不在当前目录找可执行文件,必须显式加路径。如果嫌加权限麻烦,也可以直接 bash backup.sh 执行,但定时任务里推荐用带权限的方式,更规范。
变量、条件判断与循环
脚本的核心就三样东西:变量、条件、循环。变量用来存数据,赋值语法是变量名=值,注意等号两边不能有空格,取值时在变量名前加美元符号:
#!/bin/bash
BACKUP_DIR="/www/backup"
SITE_DIR="/www/wwwroot/blog"
echo "备份目录: $BACKUP_DIR"条件判断用 if 语句,比较数字用 -gt、-lt、-eq,判断文件是否存在用 -f、-d:
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
echo "已创建备份目录"
fi循环最常用的是 for 和 while。for 遍历一组值,while 按条件重复执行:
for f in /www/backup/*.tar.gz; do
echo "找到备份文件: $f"
done写条件时有两个坑要记住:一是 [ 和 ] 两边都要留空格,写 [ -d "$DIR" ] 而不是 [-d "$DIR"];二是变量一定要加双引号,防止路径里有空格时被拆成多个参数。
实战一:网站数据自动备份脚本
第一个实战脚本解决备份问题。把网站文件和数据库分别打包,存到备份目录,同时只保留最近 7 天的备份,旧的自动删除:
#!/bin/bash
# 网站自动备份脚本
BACKUP_DIR="/www/backup"
SITE_DIR="/www/wwwroot/blog"
DATE=$(date '+%Y%m%d')
KEEP_DAYS=7
mkdir -p "$BACKUP_DIR"
# 打包网站文件
tar czf "$BACKUP_DIR/site-$DATE.tar.gz" -C "$SITE_DIR" .
# 备份数据库(以 MySQL 为例)
mysqldump -u root -p数据库密码 blogdb > "$BACKUP_DIR/db-$DATE.sql"
gzip "$BACKUP_DIR/db-$DATE.sql"
echo "备份完成: $(date '+%H:%M:%S')"
# 删除 7 天前的备份
find "$BACKUP_DIR" -name "site-*.tar.gz" -mtime +$KEEP_DAYS -delete
find "$BACKUP_DIR" -name "db-*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "已清理 $KEEP_DAYS 天前的旧备份"这个脚本把三个最常用的命令串起来了:tar 打包、mysqldump 导出数据库、find 按时间清理旧文件。注意 mysqldump 的密码直接写在脚本里有安全隐患,更稳的做法是把数据库账号信息放在独立的配置文件里,脚本用 source 引入,并且把配置文件权限设为 600,只有 root 能读。
备份脚本跑起来之后,还有一个容易被忽略的关键动作:恢复演练。定期把备份文件解压到临时目录,检查文件是否完整、数据库能否正常导入,确认备份真的可用,而不是仅仅"生成了文件"。很多站长备份做了半年,真到网站出问题时才发现备份文件损坏或导出不完整,那时候再补救就来不及了。建议每个月挑一个备份文件完整走一遍恢复流程,顺手把恢复步骤也写成一个 restore 脚本,这样真正出事时照着跑就行,不用临场回忆命令。
实战二:Nginx 日志自动清理脚本
第二个实战解决日志撑爆磁盘的问题。Nginx 访问日志默认不会自动清理,流量大一点一个月就能攒几个 GB。写一个脚本,只保留最近 7 天的日志,同时把 7 天前的日志压缩归档:
#!/bin/bash
# Nginx 日志清理脚本
LOG_DIR="/www/wwwlogs"
KEEP_DAYS=7
cd "$LOG_DIR" || exit 1
# 压缩 7 天前的日志
find . -name "*.log" -mtime +$KEEP_DAYS -exec gzip {} \;
# 删除 30 天前的压缩包
find . -name "*.gz" -mtime +30 -delete
echo "日志清理完成: $(date '+%Y-%m-%d %H:%M:%S')"这里用到了 find 的两个高级特性:-exec 把找到的每个文件传给 gzip 压缩,-delete 直接删除匹配文件。小提示:压缩完旧日志后,最好执行一下 nginx -s reopen,让 Nginx 重新打开日志文件句柄,否则 Nginx 还会往已经被压缩的文件里继续写,导致日志丢失。这条经验是不少站长踩过坑才记住的。
实战三:网站健康检查与告警脚本
第三个实战解决"网站挂了没人知道"的问题。用 curl 定时探测网站首页,连续失败就发一封邮件告警,并自动重启一下 PHP-FPM:
#!/bin/bash
# 网站健康检查脚本
URL="https://www.example.com"
FAIL_FILE="/tmp/site_fail_count"
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 10 "$URL")
if [ "$HTTP_CODE" = "200" ]; then
rm -f "$FAIL_FILE"
echo "网站正常: $(date '+%H:%M:%S')"
else
COUNT=$(cat "$FAIL_FILE" 2>/dev/null || echo 0)
COUNT=$((COUNT + 1))
echo "$COUNT" > "$FAIL_FILE"
if [ "$COUNT" -ge 3 ]; then
echo "网站连续 $COUNT 次探测失败!" | mail -s "网站告警" you@example.com
systemctl restart php-fpm
rm -f "$FAIL_FILE"
fi
fi这个脚本的思路是"连续失败才告警",避免网络抖动导致误报。$((COUNT + 1)) 是算术运算的写法,2>/dev/null 是把错误输出丢弃。邮件发送需要服务器装了 mail 命令,没有的话可以改成调用企业微信或钉钉的 Webhook 接口,原理一样,只是把 echo 的内容换成 curl POST 到 Webhook 地址。
用 crontab 让脚本定时运行
脚本写好了,剩下的交给 crontab。执行 crontab -e 编辑当前用户的定时任务,格式是"分 时 日 月 周 命令"。把上面三个脚本分别加到不同时间点:
# 每天凌晨 2 点备份
0 2 * * * /www/scripts/backup.sh >> /www/scripts/backup.log 2>&1
# 每天凌晨 4 点清理日志
0 4 * * * /www/scripts/cleanlog.sh >> /www/scripts/cleanlog.log 2>&1
# 每 5 分钟检查网站健康
*/5 * * * * /www/scripts/health.sh >> /www/scripts/health.log 2>&1几个要点:命令要写绝对路径,因为定时任务的环境变量和登录终端不一样;脚本里的变量和环境变量在 cron 下可能为空,所以脚本内尽量用绝对路径;>> 是追加输出,2>&1 把错误也写进日志,方便日后排查。改完 crontab 后可以用 crontab -l 查看生效的配置。
脚本排错技巧
脚本写多了难免出错,三个技巧能帮你快速定位问题。第一,用 bash -x 调试模式:bash -x backup.sh 会把每一步执行的具体命令打印出来,变量值一目了然,这是最常用的排错手段。第二,在脚本开头加 set -e,遇到任何命令执行失败就立即退出,防止错误继续执行产生更严重的问题;想精细控制的话可以用 set -euo pipefail 组合。第三,看日志说话:给关键步骤加 echo 输出时间戳,配合 crontab 里的重定向日志,就能还原脚本到底跑没跑、跑到哪一步挂了。遇到"脚本手动跑正常、定时任务里不工作"的经典问题,先检查绝对路径和权限,九成是这两个原因。
常见问题
问:crontab 里脚本没执行怎么办?答:先手动跑一遍确认脚本本身没问题,再检查 crontab -l 里是否保存成功、路径是否为绝对路径、脚本是否有执行权限,最后看重定向的日志文件里有没有报错信息。
问:脚本里有中文乱码怎么办?答:在脚本头部加 export LANG=zh_CN.UTF-8 或者 export LC_ALL=C,保证编码一致;文件本身要保存为 UTF-8 格式。
问:定时任务一分钟跑一次会不会太频繁?答:健康检查这类轻量任务没问题,但备份、清理类任务别设太频繁,否则可能和业务高峰重叠;另外同一时间点别堆太多任务,错开分钟数更稳。
问:脚本里密码怎么管理更安全?答:不要把密码硬编码在脚本里,单独建一个权限为 600 的配置文件,脚本用 source 引入;敏感脚本权限设为 700,只允许 root 执行。
总结
Shell 脚本是个人站长性价比最高的技能之一:入门成本低,回报立竿见影。备份、日志清理、健康检查、定时任务,这些日常运维里最琐碎的事,几十行脚本就能全部自动化,从此告别半夜被磁盘告警吵醒的日子。建议你先从备份脚本抄起来,跑通一个,再逐步加上自己的需求,慢慢就能写出适合自己网站的整套自动化脚本。技术这东西,动起手来才会真正变成自己的。