直接查文件被谁打开应使用 lsof /path/to/file,需完整精确路径并常需 sudo;lsof +d 递归查目录但不跨挂载点;lsof +L1 查已删仍占空间的文件;lsof -t 用于脚本静默判断占用。

直接查文件被谁打开:lsof /path/to/file
这是最直白的做法。只要文件存在且路径准确,lsof /var/log/messages 就会列出所有打开它的进程。注意:路径必须完整、精确,不能是相对路径或带通配符的模糊写法。
常见错误现象是返回空结果,但文件明明在被写入——这通常是因为你没加 sudo。普通用户无法读取其他用户进程的 /proc/PID/fd/ 目录,所以看不到 root 或 daemon 进程打开的文件。遇到空输出,第一反应是改用 sudo lsof /path/to/file。
-
lsof输出中FD列为txt表示该进程正在执行这个文件(如二进制);REG类型 +w或u表示正在读写普通文件;cwd表示只是当前工作目录,未必真在操作该文件 - 如果文件已被
rm删除但仍在被进程持有(常见于日志轮转后),lsof仍能查到,NAME 列会显示/path/to/file (deleted) - 不建议在生产环境无差别运行
lsof | grep xxx——它扫描全系统 fd,开销大、慢,且容易漏掉符号链接或 bind mount 下的真实路径
查整个目录及其子目录下的占用:lsof +d /path/to/dir
+d 是递归扫描目录的专用选项,比 lsof | grep /path/to/dir 更可靠:它只检查内核实际打开的路径,不依赖字符串匹配,不会误报同名路径(比如 /opt/app/logs 和 /opt/app-legacy/logs 不会混淆)。
但它有个硬限制:不进入子挂载点。例如 /mnt/data 下挂了另一个文件系统,+d /mnt/data 不会扫描那个挂载点里的文件。需要单独对子挂载点再跑一次。
-
+d只扫描一级子目录,不支持深度控制(没有++d或-maxdepth类参数) - 若目录权限受限(如
dr-xr-x---),即使加sudo,lsof也可能跳过某些子目录——它依赖opendir()成功,不是靠暴力遍历 - 输出可能很长,建议配合
| head -20或| awk '{print $1,$2,$3,$9}'快速聚焦关键列
查已删除但仍被占用的文件:lsof +L1
磁盘空间释放不了?很可能是文件被删了但进程还握着 fd。lsof +L1 专门列出 link count 为 0 的文件(即已 unlink 但未 close 的文件)。NAME 列末尾会明确标出 (deleted)。
这个命令不需要指定路径,适合全局排查。但要注意:它只报告“已删除”,不告诉你原始路径是否还在目录树里——有些程序会把临时文件建在 /tmp 后立刻 unlink,这种属于正常行为,不必干预。
- 输出中的
SIZE/OFF列显示当前文件逻辑大小,可用来判断是否是日志类长连接写入(持续增长) - 若发现某个 Java 进程占着几 GB 的
(deleted)文件,大概率是 logback 或 log4j 的滚动配置异常,需检查appender的file和rollingPolicy -
+L1对 NFS 或 overlayfs 等特殊文件系统支持有限,部分已删文件可能不会出现
快速判断能否安全删除或卸载:lsof -t 静默模式
脚本里做自动化判断时,别解析完整输出。lsof -t /path 只返回 PID 列表(每行一个数字),没有标题、没有空格、没有额外文本。返回空表示没人占用,返回非空表示有进程在用。
配合 shell 逻辑非常干净:if [ -z "$(lsof -t /data/db)" ]; then rm -rf /data/db; fi。但得小心:-t 不加 sudo 依然只能看到当前用户进程,权限问题和 fuser -s 一样存在。
-
lsof -t不等价于lsof -s(后者是 fuser 的静默开关) - 它不区分访问类型——哪怕只是
cwd,也算“占用”,可能导致误判。真要排除 cwd,得加-F p配合过滤,但那就脱离“快速判断”初衷了 - 和
fuser -s相比,lsof -t更重一点(启动慢、内存多),但胜在语义明确、不发信号、无副作用
mount --bind 或容器 volume 暴露进来的路径——lsof 显示的是最终 inode 路径,不是挂载源路径。这时候得先用 findmnt 或 ls -l /proc/*/root 锁定容器或命名空间上下文,再针对性查。

















