磁盘空间告急别急着 rm。先用 du 和 find 定位,再用 lsof 查”删了没释放”,最后按类型动手,关键命令均在 WSL 实测。
一、先分清口径:du 数磁盘占用,find 数文件字节
清理前的第一个决定就有人做错:du 报的是文件**占用的磁盘块**,find -size 用的是 st_size,也就是 ls -l 那种**文件字节数** [1],两者对稀疏文件会完全对不上。在测试目录里造了四种文件后实测:
| 文件 | 造法 | du -sh | find -size +100M | | — | — | — | — | | alloc.bin | fallocate 300M | 301M | 命中(314572800) | | sparse.bin | truncate 500M | **0** | **命中**(524288000,排第 1) | | real.bin | dd 写 200M | 200M | 命中(209715200) | | mid.bin | 写入 1.5M | 1.5M | 不命中 |
所以按”谁真的吃磁盘”清理要看 du,按”文件名义大小”排查(比如传输、配额)才看 find。日常定位大目录:
du -xh --max-depth=1 / 2>/dev/null | sort -rh | head # -x 不跨挂载点,逐个目录下钻
du -sh ./* 2>/dev/null | sort -rh | head # 当前目录下逐项排序
排序靠 GNU sort 的 -h;busybox 等精简系统没有这个参数,可退回 du -k 再 sort -rn(此为文档口径,精简系统未实测)。
二、find 定位大文件:取整规则和 -t 的坑
find / -xdev -type f -size +100M -printf '%s\t%p\n' | sort -rn | head -20
find /var -type f -name '*.log' -mtime +30 -size +10M # 只要 30 天没改过的大日志
du -ah -t 250M /var 2>/dev/null | sort -rh | head # du 版:≥250M 才显示
三个实测确认的细节:
三、确认之后再删:-delete 的安全用法
手册建议的稳妥流程是:同样的条件先跑 -print,人工核对一遍,再把 -print 换成 -delete [1]。
find /var/cache/app -type f -mtime +30 -name '*.tmp' -print # 第一步:只看,不删
find /var/cache/app -type f -mtime +30 -name '*.tmp' -delete # 第二步:删除,文件消失,退出码 0
find /var/cache/app -depth -type d -empty -delete # 第三步:清空的目录再来一遍
实测确认三点:-delete 隐含 -depth,目录**只有为空时**才会被删,非空目录会留在原地;手册同时警告它和 -prune 的组合会有不可预料的后果,不要搭配使用。两句均为 find 手册口径 [1]。
四、明明删了大文件,df 却纹丝不动
这是”清理无效”的头号原因:文件已删,但还有进程握着它的文件描述符,POSIX 要等最后一个 fd 关闭才真正释放空间。实测:300M 文件被 sleep 进程打开后 rm,df 空闲量分毫未动;lsof 立刻抓到它:
lsof +L1 # NLINK 列为 0、路径带 (deleted) 的就是元凶
ls -l /proc/<pid>/fd # 备选:从进程侧看它握着的已删文件
systemctl restart <服务名> # 让持有它的服务重开文件,空间当场归还
实测数据:300M 文件 rm 后 df 空闲量不变,lsof +L1 显示 sleep 进程 3 号 fd 持有该文件(NLINK 0,SIZE/OFF 314572800);杀掉持有进程后 df 空闲量恢复原值。+L1 的语义就是”挑出已被 unlink 的打开文件” [3]。
五、正在写的日志:用 truncate 清零,别直接 rm
对活跃日志直接 rm,实测后果就是上一节:150M 日志删掉后 df 不降,直到写日志的进程退出才归还。正确姿势是保留 inode、把内容截断:
truncate -s 0 /var/log/app/access.log # 文件在、fd 在、空间立刻回
journalctl --disk-usage # 看 journal 总量(实测输出 808.0M)
journalctl --vacuum-size=200M # 按手册口径清理,本机未实际执行
truncate -s 0 实测:200M 日志清零后 df 空闲量立即 +200M,写进程毫无察觉。journal 方面,手册提醒 –vacuum-size 只删**已归档**的 journal 文件,–disk-usage 还包含活动文件,所以跑完 vacuum 数字不一定同步下降 [6]。
六、想要可视化:ncdu
ncdu 是 ncurses 界面的磁盘占用浏览器,官方定位就是”在没图形界面的机器上快速找出吃了多少空间” [4]。Ubuntu 24.04 里 apt 可直装(实测装到 1.19-0.1),Debian trixie 的包已到 1.22-1 [5]。实测确认它的无终端导出模式可用:ncdu -x -o out.json /root/bigT 退出码 0,JSON 里每个条目同时带 asize(名义大小)和 dsize(磁盘占用),正好对应第一节的两个口径。交互式界面的操作手感非终端环境无法实测,未验证。
| 现象 | 原因 | 处理 | | — | — | — | | du 扫遍全盘找不到大块头,df 却见少 | 已删除未释放 | lsof +L1 找持有进程,重启服务 | | find -size 命中,du -sh 却是 0 | 稀疏文件,口径不同 | 对比 du –apparent-size 再决定删不删 | | -delete 跑完空目录还在 | 目录非空时不删 | 补一遍 -depth -type d -empty -delete | | du -t 250M 什么都没输出 | 默认只统计目录 | 加 -a 看文件级 | | sort: invalid option — ‘h’ | 非 GNU sort | du -k 配 sort -rn |
**常见问题**
局限性:本文测试环境为 WSL2 Ubuntu 24.04(findutils 4.9.0、coreutils 9.4、lsof 4.95、ncdu 1.19,测试日期 2026-10-27),单 ext4 文件系统;systemd journal 的 vacuum 仅做了 –disk-usage 实测,删除动作基于手册未执行;RPM 系发行版、SELinux 环境及真实生产磁盘未覆盖;sort -h 缺失行为、ncdu 交互界面均为文档口径。
**实测记录**
参考资料
- [1] find — Debian manpages (findutils)(Debian find(1))
- [2] du invocation (GNU Coreutils manual)(GNU du 手册)
- [3] lsof 8 — Ubuntu manpages(Ubuntu lsof(8))
- [4] ncdu — Name's code dupe looked familiar, right? (official page)(ncdu 官网)
- [5] ncdu — Debian package details (trixie)(Debian ncdu 包)
- [6] journalctl(1) — systemd manual(journalctl 手册)
文中主要命令已在 WSL Ubuntu(24.04)实测并核对其在 Debian 系与通用发行版的文档口径。