这个场景解决什么
磁盘告急时最怕手一抖删错东西,所以清理不是一个 rm 能了事,而是四步走:按时间条件圈定候选、干跑核对清单与占用总量、把「还想留但要省空间」的日志压缩成 .gz、把「已经过期」的归档删掉,最后核对空间回收量和文件完整性。全程用 NUL 分隔传递文件名(find -print0 配 xargs -0),带空格、引号甚至换行的文件名都不会被拆裂成多个参数。
基本信息
| 适用场景 | 应急清理某个日志或临时目录;接管没人管的脏目录并建立留存规则 |
|---|---|
| 核心组合 | find 圈定时间范围 + du -ch --files0-from 统计 + xargs -0 + gzip/rm |
| 前置动作 | df -h 确认哪个文件系统满,df -i 排除 inode 耗尽(这时删文件才有用) |
| 风险等级 | 高:删除不可逆。必须先干跑看清单,再执行动作 |
典型使用场景
- 应急腾空间:/var/log 或某应用目录涨满,业务低峰期需要尽快回收容量
- 接管脏目录:遗留目录长期没人管,需要建立分级留存规则(近 30 天原文、180 天内 .gz)
- 搬迁前瘦身:整个目录要打包传输,先压缩历史文件减小体积
- 审计前留证:保留期内的文件不删只压,保证仍能用 zcat 追溯内容
- 固化成任务:把验证过的命令写进 cron,替代每次都人工登录处理
第 1 步:干跑核对
第 1 步:干跑核对
# 1) 先分清是容量满还是 inode 满,处理方式完全不同
df -h /var/log; df -i /var/log
# 2) 圈定候选:只打印清单,不执行任何动作
find /var/log/app -type f -name '*.log' -mtime +30 -print | head -50
# 3) 统计候选总量(前面用了 -print0,这里就配 --files0-from)
find /var/log/app -type f -name '*.log' -mtime +30 -print0 | du -ch --files0-from=- 2>/dev/null | tail -1
# 4) 确认候选里没有仍被进程打开的日志(正在写的别动)
find /var/log/app -type f -name '*.log' -mtime +30 -print0 | xargs -0 -r lsof 2>/dev/null | head第 2 步:压缩历史日志
第 2 步:压缩历史日志
# -r 让候选为空时不执行,避免报错或被当成错误退出
find /var/log/app -type f -name '*.log' -mtime +30 -print0 | xargs -0 -r gzip -9
# 校验压缩包完整性:zcat -t 只测试不解压输出
zcat -t /var/log/app/app.log.2026-08-01.gz
# 看压缩比与回收情况
du -sh /var/log/app; df -h /var/log第 3 步:删除更老的归档
第 3 步:删除更老的归档
# 只删 .gz,避免误伤刚产生的新日志
find /var/log/app -type f -name '*.log.gz' -mtime +180 -print | head
find /var/log/app -type f -name '*.log.gz' -mtime +180 -print0 | xargs -0 -r rm -v --
# 顺手清掉长期空目录(确认无人在用再删)
find /var/log/app -type d -empty -mtime +180 -print0 | xargs -0 -r rmdir第 4 步:留痕与定时
第 4 步:留痕与定时
# 每步都追加到运维日志,事后能回答「删了哪些文件」
{ date -Is; df -h /var/log; } >> /var/log/ops/clean-$(date +%F).log
# 常态化为每日低峰任务(命令写进脚本再挂 crontab)
# 10 4 * * * /opt/ops/log-clean.sh >> /var/log/ops/log-clean.log 2>&1建议的分层留存策略
| 文件年龄 | 动作 | 为什么 |
|---|---|---|
| 0-7 天 | 不动 | 当前滚动日志,进程正在写入,动它会导致句柄失效 |
| 7-30 天 | 保留原文 | 近期排障还要反复 grep,压缩后虽然能 zcat 但不如原文方便 |
| 30-180 天 | gzip -9 压缩 | 文本日志压缩比通常能达到 5:1 以上,仍可追溯 |
| 180 天以上 | 删除 | 删除前核对合规与审计要求;只删 .gz,别碰新日志 |
长期方案是 logrotate 负责滚动与压缩、find 只兜底处理它管不到的目录。两套规则并行时,确保它们不对同一批文件重复动手,否则会出现「刚压完又被解压源覆盖」这类混乱。
脚本里用到的目录变量要先判空并确认不是根目录,再执行删除;变量为空时路径会被拼成 /,find 与 rm 组合会变成灾难。