ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Shell自动化脚本实战:日志清理、数据备份、批量部署

2026/10/8 13:37:26 拓冰建站 浏览量
Shell自动化脚本实战:日志清理、数据备份、批量部署 前面几篇把输入输出、重定向、信号、调试拆开讲了这一篇把它们攒成能跑的工具。三个脚本定时清理旧日志、打包备份数据、批量往多台机器推配置。每个都带完整代码和实际输出拷出去改改路径就能挂 cron。一、日志清理脚本需求每天凌晨清一次某目录下的.log。超过 7 天的压缩成.gz超过 30 天的.gz直接删。#!/bin/bashset-euopipefailLOG_DIR/var/log/myappKEEP_DAYS7GZIP_DAYS30cd$LOG_DIR||exit1echo[$(date%F %T)] 开始清理$LOG_DIR# 压缩 7 天前、还没压缩的日志find$LOG_DIR-maxdepth1-name*.log-typef-mtime$KEEP_DAYS!-name*.gz|whileread-rf;dogzip$fecho压缩:$fdone# 删除 30 天前的 .gzfind$LOG_DIR-maxdepth1-name*.gz-typef-mtime$GZIP_DAYS-print-deleteecho清理完成剩余占用:$(du-sh$LOG_DIR|cut-f1)预期输出[2026-09-16 03:00:01] 开始清理 /var/log/myapp 压缩: /var/log/myapp/app-2026-09-08.log 压缩: /var/log/myapp/app-2026-09-09.log /var/log/myapp/app-2026-08-10.log.gz 清理完成剩余占用: 42M几个细节-maxdepth 1别递归进子目录-mtime 7是修改时间超过 7*24 小时不是整 7 天前零点管道喂while read时循环里的变量出循环就丢这里没在循环外用没事。crontab 里挂0 3 * * * /opt/scripts/logclean.sh /var/log/logclean.cron.log 21二、数据备份脚本备份某个目录打tar.gz保留最近 5 份更老的删掉。打完包立刻校验防止留个坏包。#!/bin/bashset-euopipefailSRC/data/mysqldumpDEST/backupRETENTION5TS$(date%Y%m%d-%H%M%S)ARCHIVE$DEST/db-$TS.tar.gzecho[$(date%F %T)] 备份$SRC-$ARCHIVEtar-czf$ARCHIVE-C$(dirname$SRC)$(basename$SRC)# 校验能列出来才算成功if!tar-tzf$ARCHIVE/dev/null;thenecho备份文件损坏删除$ARCHIVE2rm-f$ARCHIVEexit1fiSIZE$(du-h$ARCHIVE|cut-f1)echo备份完成:$ARCHIVE($SIZE)# 只留最近 RETENTION 份cd$DEST||exit1ls-1tdb-*.tar.gz|tail-n$((RETENTION1))|whileread-rold;doecho删除旧备份:$oldrm-f$olddone预期输出[2026-09-16 03:10:00] 备份 /data/mysqldump - /backup/db-20260916-031000.tar.gz 备份完成: /backup/db-20260916-031000.tar.gz (1.2G) 删除旧备份: /backup/db-20260911-031000.tar.gzls -1t按时间倒序列tail -n 6从第 6 行开始全是该删的。tar 打完包立刻tar -tzf试列一遍防止磁盘写一半脚本就挂留个坏备份在那。-C切换打包根目录这样解包出来不带/data那一长串绝对路径。三、批量部署把配置推到多台机器用 ssh 把 nginx 配置同步到一组服务器逐台报告成功失败先推.new再原子替换配置语法通过才 reload。#!/bin/bashset-euopipefailFILE/etc/nginx/nginx.confHOSTS(web01 web02 web03)KEY/root/.ssh/deploy_keyforhostin${HOSTS[]};doecho---- 部署到$host----ifscp-i$KEY-oBatchModeyes-oConnectTimeout5$FILEroot$host:/etc/nginx/nginx.conf.new;thenssh-i$KEY-oBatchModeyesroot$hostmv /etc/nginx/nginx.conf.new /etc/nginx/nginx.conf nginx -t systemctl reload nginxechoOK:$hostelseechoFAIL:$host连接或拷贝失败2fidone预期输出---- 部署到 web01 ---- OK: web01 ---- 部署到 web02 ---- OK: web02 ---- 部署到 web03 ---- FAIL: web03 连接或拷贝失败BatchModeyes禁止 ssh 交互输入密码连接不上直接失败脚本不会卡在那等人输密码。先推到.new再mv替换nginx -t校验配置语法通过才 reload万一语法错旧文件没动服务不受影响。机器多、要并行把 for 换成xargs -Pprintf%s\n${HOSTS[]}|xargs-P5-I{}sh-cecho deploy {}; scp -i /root/.ssh/deploy_key /etc/nginx/nginx.conf root{}:/etc/nginx/nginx.conf.new-P 5同时跑 5 个机器越多省时间越明显。四、批量健康检查一组机器的服务状态部署完顺手跑一遍检查看看 nginx 是不是真活了#!/bin/bashset-uHOSTS(web01 web02 web03)KEY/root/.ssh/deploy_keyforhostin${HOSTS[]};docode$(ssh-i$KEY-oBatchModeyes-oConnectTimeout5root$host\curl -s -o /dev/null -w %{http_code} http://127.0.0.1/health2/dev/null)if[$code200];thenecho$hostOKelseecho$hostFAIL (http$code)fidone预期输出web01 OK web02 OK web03 FAIL (http000)http000表示 curl 根本没拿到响应服务没起或者端口不通code为空时[ $code 200 ]在set -u下会报错所以这个例子开头只开了-u没加-e——检查脚本故意要把失败跑完。五、⚠️ 脚本上线前的检查清单开头写set -euo pipefail关键命令后面|| exit 1兜底。所有变量加引号$var路径含空格才不会炸。用绝对路径或者开头cd $(dirname $0)切到自己目录。临时文件用mktemptrap EXIT 清理。重定向到日志时追加别覆盖。shellcheck过一遍没有 SC 报错再上线。六、知识扩展cron 里的脚本为什么和手动跑不一样手动敲./logclean.sh没事挂到 cron 就报错——十有八九是环境变量不一样。cron 给的 PATH 很短通常只有/usr/bin:/bin。你手动能用的gzip、tar都在这俩目录里没事但如果脚本里调了自己装的/usr/local/bin/xxx或者 nvm 里的 nodecron 下就找不到。解决办法二选一脚本开头写死 PATHexportPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin或者命令全用绝对路径。第二个坑是没有 tty。交互式脚本里read、tput颜色、ssh 密码提示全失效。cron 下要避免这些ssh 用密钥 BatchModeyes颜色判断加[ -t 1 ]。第三个坑是时区和工作目录。cron 默认从/开始跑相对路径全错位时区如果系统是 UTC你以为凌晨 3 点跑实际可能是上午 11 点。脚本里一律用绝对路径需要定时的时间到date看一眼系统时区timedatectl确认。