写定时清理日志脚本,真正要先定的是三件事:清哪个目录、按时间还是按大小删、被删的文件是不是还被进程打开着。这三件事没确认就把 find ... -delete 塞进 crontab,常见结果是文件从目录里消失了,df -h 却没降。下面按排查顺序给出可以直接复制的做法。
定时清理日志建议先确认三件事:日志目录与文件名规则、删除条件(按时间或按大小)、文件是否被进程持有。做法上优先用 find 按 mtime 清理,跳过正在写的固定名文件,删前先清空内容;执行后用 df -h 与 du -sh 对照、lsof +L1 查残留句柄来验证。如果日志已由 logrotate 管理,应先改 /etc/logrotate.d 下的配置,而不是另加一套 cron 删除。
定时清理日志脚本:先把能核对的事实摆清楚
先在目标机器上把下面几项核对一遍,都能直接看到输出:
- 日志在哪、多大、命名是否带日期:
ls -lh /var/log/myapp、du -sh /var/log/myapp/*。文件名形如app-2024-05-01.log,说明应用自己按天切;只有一个app.log,说明是固定名且正在被写。 - 是否已经由 logrotate 接管:
ls /etc/logrotate.d/,再用logrotate -d /etc/logrotate.d/myapp演练(-d 只打印不执行)。 - 定时任务是否可用:
crontab -l、systemctl status cron或systemctl status crond,看服务是否为 active。 - 磁盘与目录用量是否对得上:
df -h /var对比du -sh /var/log。
这几项输出就是写脚本的依据:命名规则决定 find 的匹配条件,logrotate 是否已存在决定要不要另写脚本,cron 服务状态决定脚本放 crontab 还是 systemd timer。这一段先不下结论,是因为同一台机器上 /var/log 里的系统日志和业务日志往往归不同机制管,不能一刀切。
定时清理日志脚本最容易搞错的一点
最常见的误解是:删掉日志文件,磁盘空间就回来了。文件被删只是目录项没了,如果仍有进程打开着它(fd 还在),inode 和数据块不会释放,df -h 照旧是满的。

区分这两种情况的检查动作:清理之后同时执行 df -h /var 和 du -sh /var/log。如果 du 明显小于 df,说明有文件已被删除但句柄未释放,再用 lsof +L1 2>/dev/null | head 或 ls -l /proc/$(pgrep -f myapp | head -1)/fd | grep deleted 确认;反过来 du 与 df 基本吻合,就说明空间确实回收了。
处理方向也随之分开:句柄未释放时,正确做法是先清空内容(: > file 或 truncate -s 0 file)再决定要不要删,或者让 logrotate 用 copytruncate,或者重载服务让它重新打开文件;单纯 rm 只会让目录看起来干净。固定文件名的当前日志,通常不建议进入删除集合。
定时清理日志脚本:一个照着就能做完的做法
分三步落到一台机器上,先演练再上定时。

第一步,写脚本 /usr/local/bin/clean-logs.sh,只需要改 LOG_DIR、KEEP_DAYS 和匹配规则:
#!/bin/bash
set -u
LOG_DIR="/var/log/myapp"
KEEP_DAYS=14
PATTERN='*.log.*' # 只清归档文件,避免动到正在写的 app.log
DRY_RUN="${DRY_RUN:-0}"
[ -d "$LOG_DIR" ] || { echo "$(date '+%F %T') 目录不存在: $LOG_DIR"; exit 1; }
find "$LOG_DIR" -maxdepth 1 -type f -name "$PATTERN" -mtime +"$KEEP_DAYS" -print0 |
while IFS= read -r -d '' f; do
if [ "$DRY_RUN" = "1" ]; then
echo "[dry-run] $f"
continue
fi
: > "$f" && rm -f "$f" && echo "$(date '+%F %T') 已清理 $f"
done
第二步,演练。执行 bash -n /usr/local/bin/clean-logs.sh 应无输出(语法通过),再执行 DRY_RUN=1 /usr/local/bin/clean-logs.sh,屏幕上应出现一批 [dry-run] /var/log/myapp/... 路径,逐个确认都是可以删的归档文件;候选集合不对就回头改 PATTERN 或 KEEP_DAYS。
第三步,正式执行并挂定时任务。先 chmod +x /usr/local/bin/clean-logs.sh,手动跑一次确认输出里有「已清理」行,再 crontab -e 加一行:

30 3 * * * /usr/local/bin/clean-logs.sh >> /var/log/clean-logs.log 2>&1
cron 环境的 PATH 比登录 shell 短,脚本里用到外部命令尽量写绝对路径,或在脚本开头加 PATH=/usr/bin:/bin。保存后用 crontab -l 确认条目在。
定时清理日志脚本:怎么验证结果对不对
等下一次触发时间过后,按顺序验证:
tail -n 20 /var/log/clean-logs.log:正常结果是若干行带时间戳的「已清理 /var/log/myapp/xxx」,没有 command not found、Permission denied 这类报错。find /var/log/myapp -maxdepth 1 -name '*.log.*' -mtime +14 -print:正常应无任何输出,说明超过保留期的归档文件已不存在。ls -lh /var/log/myapp:正在写的 app.log 应当还在,且大小正常增长。df -h /var与du -sh /var/log/myapp对照:两者差值应回到合理范围;如果 df 没降,回到上一节用lsof +L1查残留句柄。
cron 有没有真的执行,可以看 journalctl -u cron --since today(Debian/Ubuntu 系)或 grep clean-logs /var/log/cron(RHEL 系),日志具体落在哪个文件要以本机 /etc/rsyslog.conf 或 /etc/systemd/journald.conf 的配置为准。若脚本日志里有记录但文件没变,优先怀疑 PATTERN 写得太窄或 mtime 条件没到,而不是 cron 没跑。