Skip to content
This repository was archived by the owner on Aug 13, 2026. It is now read-only.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wholock

Who's locking this file? Find the process that's holding your file hostage — and free it.
谁占用了我的文件? 一条命令找出锁住文件的进程,并解除占用。

CI License: MIT Python 3.9+ Platforms Zero dependencies

English | 中文


You know these errors:

The process cannot access the file because it is being used by another process.  (Windows)
PermissionError: [WinError 32]                                                   (Python on Windows)
rm: cannot remove 'data.db': Device or resource busy                             (Linux)
OSError: [Errno 16] Device or resource busy                                      (Python on Linux)

They all mean the same thing: some process is holding the file. But which one? That's the question wholock answers — in one command, on Windows, Linux, and macOS, with zero dependencies.

$ wholock report.xlsx
report.xlsx - locked by 1 process:
  PID    NAME       KIND    VIA     EXE
  17048  EXCEL.EXE  window  handle  C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE

$ wholock --kill report.xlsx     # terminate it (asks first)
$ wholock --wait -t 30 out.dll   # or wait until it's released

Install

pip install wholock        # or: pipx install wholock

No compiler, no admin rights, no other packages — it's pure standard library.

Until the PyPI release is live you can also install straight from GitHub:

pip install git+https://github.com/hc-ui/wholock

Why another tool?

wholock PowerToys File Locksmith Sysinternals Handle openfiles.exe Resource Monitor fuser / lsof psutil script
Windows
Linux / macOS
Install pip install full PowerToys install manual download built-in built-in built-in needs compiled dep
Works without admin ✅ usually partially ❌ admin required ❌ global flag + reboot partially partially
Scriptable JSON CSV ❌ (GUI) DIY
Kill / wait built in ✅ both ✅ both kill only kill only (fuser -k) DIY
Python API ~
Open source ✅ MIT

The niche wholock fills: one pip install that works on all three platforms, needs no elevation for everyday cases, and is also a library you can call from your own Python code, tests, and CI.

CLI

wholock [options] PATH [PATH ...]

output:
  -j, --json           machine-readable JSON output
  --pids               print only the PIDs, space-separated (fuser-style)
  -q, --quiet          no output, just the exit code
  --no-color           disable colors (also respects NO_COLOR)

actions:
  -k, --kill           terminate the locking processes
  -y, --yes            don't ask for confirmation before killing
  -f, --force          SIGKILL instead of SIGTERM (POSIX); allow killing critical processes
  -w, --wait           wait until the path(s) are free
  -t, --timeout SECS   give up waiting after SECS (default: wait forever)
  --interval SECS      polling interval for --wait (default: 0.5)

Exit codes (script-friendly, fuser-inspired):

Code Meaning
0 lockers found (query) / path freed (--kill, --wait)
1 nothing is holding the path(s)
2 error: missing path, permission problem, bad usage
3 --wait timed out
4 --kill could not free the path(s)

Examples:

wholock data.db                          # who is holding it?
wholock --json data.db | jq '.results[0].lockers[].pid'
wholock --pids data.db                   # e.g. "1234 5678"
wholock -k -y build/output.dll           # free it, no questions asked
wholock -w -t 60 video.mp4 && mv video.mp4 done/   # wait, then act
wholock ~/Downloads                      # directories work too

Python API

The part no other tool has. Ever seen this in your logs?

PermissionError: [WinError 32] The process cannot access the file because
it is being used by another process: 'C:\\app\\cache.db'

Now you can log who:

import os
import wholock

try:
    os.replace("cache.db.tmp", "cache.db")
except PermissionError:
    raise RuntimeError(wholock.explain("cache.db"))
    # RuntimeError: cache.db is in use by antivirus.exe (pid 4242)

Full surface — four functions, one dataclass:

import wholock

# 1) Query: list of Locker(pid, name, exe, kind, via, user, started)
for p in wholock.who_locks("report.xlsx"):
    print(p.pid, p.name, p.via)

# 2) Explain: one-line summary, never raises on missing files
msg = wholock.explain("report.xlsx")

# 3) Wait: block until the file is released (True) or timeout (False)
if wholock.wait_until_free("report.xlsx", timeout=30):
    process_file("report.xlsx")

# 4) Kill: terminate the holders (with PID-reuse protection)
for locker, ok, message in wholock.kill_lockers("report.xlsx"):
    print(locker.pid, ok, message)

Typical uses: retry loops around os.replace, cleaning up temp dirs in test teardown on Windows, CI steps that wait for a build artifact to be released, better error messages in installers and updaters.

How it works

OS Backend Notes
Windows Restart Manager API (Rstrtmgr.dll via ctypes) The official mechanism behind Windows' own "file in use" dialogs. Fast, race-free, and usually works without admin.
Linux direct /proc scan No lsof needed. Detects open fds, memory maps, cwd, exe, and chroot references.
macOS system lsof (ships with macOS) Machine-readable -F output parsing.

The via field tells you how a process holds the path: handle (Windows), open (fd), mmap (memory-mapped), cwd (it's the process's working directory), exe (it's the running binary), root (chroot).

Safety rails when killing:

  • Never kills the calling process or system PIDs (≤ 4).
  • Windows/Linux: verifies the process start time before killing, so a recycled PID is never killed by mistake.
  • Windows-critical processes are skipped unless you pass --force.
  • Zombie processes are treated as already dead (they hold nothing).

Limitations (honesty section)

  • Processes of other users may be invisible without elevation (sudo / administrator). wholock warns when that could be the reason you see zero results.
  • On Windows, querying a directory checks the files inside it (up to 10,000). A process merely cd-ed into the folder holds no file handle Restart Manager can see — if a folder won't delete, look for Explorer windows or terminals sitting in it.
  • The Windows Restart Manager cannot inspect paths ≥ 260 characters (verified experimentally — the \\?\ prefix does not help). wholock raises a clear error for such files and skips them with a warning when they appear inside a queried directory.
  • On macOS, scanning big directories can be slow — that's lsof +D.
  • Files on network shares are only as visible as the OS makes them.
  • --kill on Windows is always forceful (TerminateProcess); Windows has no polite cross-process SIGTERM.

FAQ

Why not just use psutil? psutil is a fine library, but: it's a compiled dependency (~x MB, needs wheels), finding a file's holder means scanning open_files() of every process (slow, often permission-blocked, misses memory-mapped files), and there's no CLI. wholock asks the OS the way the OS wants to be asked.

Do I need admin/root? Usually not for your own processes — which covers the typical "my build can't overwrite a DLL" and "Excel still has the CSV" cases. System services and other users' processes may need elevation to show up.

Can it close a single handle instead of killing the process? No, by design. Force-closing a handle inside a live process (what Sysinternals Handle -c does) can corrupt that process's state. Kill or wait — both are safe.

Is it fast? Windows: a Restart Manager session takes a few ms per file. Linux: one pass over /proc. macOS: one lsof invocation.

Contributing

Bug reports and PRs welcome. The test suite runs on every push across Windows / Linux / macOS × Python 3.9–3.14:

python -m unittest discover -s tests -t . -v

If wholock saved your file (or your afternoon), a ⭐ helps others find it.

License

MIT


wholock 中文文档

English | 中文

你一定见过这些报错:

另一个程序正在使用此文件,进程无法访问。                    (Windows)
PermissionError: [WinError 32]                              (Windows 上的 Python)
rm: cannot remove 'data.db': Device or resource busy        (Linux)
OSError: [Errno 16] Device or resource busy                 (Linux 上的 Python)

意思都一样:有个进程占着你的文件。但到底是哪个? wholock 一条命令给你答案——Windows / Linux / macOS 全平台,零依赖

$ wholock 报告.xlsx
报告.xlsx - locked by 1 process:
  PID    NAME       KIND    VIA     EXE
  17048  EXCEL.EXE  window  handle  C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE

$ wholock --kill 报告.xlsx     # 直接结束占用进程(会先确认)
$ wholock --wait -t 30 out.dll # 或者等它自己释放

安装

pip install wholock        # 或 pipx install wholock

纯 Python 标准库实现:不需要编译器、不需要管理员权限、不装任何第三方包。

PyPI 发布前也可以直接从 GitHub 安装:

pip install git+https://github.com/hc-ui/wholock

为什么又造一个轮子?

wholock PowerToys File Locksmith Sysinternals Handle openfiles 资源监视器 fuser / lsof psutil 脚本
Windows
Linux / macOS
安装方式 pip install 要装整个 PowerToys 手动下载 系统自带 系统自带 系统自带 需编译依赖
免管理员 ✅ 多数场景 部分 ❌ 必须管理员 ❌ 要开全局标志并重启 部分 部分
JSON 输出 CSV ❌(图形) 自己写
内置杀进程/等待 ✅ 都有 ✅ 都有 只能杀 fuser -k 只能杀 自己写
Python API ~
开源 ✅ MIT

wholock 填补的空缺:一条 pip install 三平台通用、日常场景免提权, 而且是唯一能当 Python 库调用的——在你自己的代码、测试、CI 里直接查占用。

命令行用法

wholock [选项] 路径 [路径 ...]

输出:
  -j, --json           输出机器可读的 JSON
  --pids               只输出 PID,空格分隔(fuser 风格)
  -q, --quiet          不输出任何内容,只返回退出码
  --no-color           关闭彩色输出(也支持 NO_COLOR 环境变量)

动作:
  -k, --kill           结束占用进程
  -y, --yes            杀进程前不再询问确认
  -f, --force          POSIX 上用 SIGKILL 代替 SIGTERM;并允许结束关键进程
  -w, --wait           等待路径被释放
  -t, --timeout 秒     等待超时时间(默认:一直等)
  --interval 秒        轮询间隔(默认 0.5)

退出码(方便写脚本,参考 fuser 惯例):

含义
0 查到占用进程(查询模式)/ 路径已释放(--kill--wait)
1 没有任何进程占用
2 错误:路径不存在、权限问题、参数错误
3 --wait 等待超时
4 --kill 没能释放路径

常用示例:

wholock data.db                          # 谁占着它?
wholock --json data.db | jq '.results[0].lockers[].pid'
wholock --pids data.db                   # 输出如 "1234 5678"
wholock -k -y build/output.dll           # 直接释放,不询问
wholock -w -t 60 视频.mp4 && mv 视频.mp4 done/   # 等释放后再操作
wholock ~/Downloads                      # 目录也支持

Python API(其他工具都没有的部分)

日志里见过这个吗?

PermissionError: [WinError 32] The process cannot access the file because
it is being used by another process: 'C:\\app\\cache.db'

现在可以把"凶手"也记进日志:

import os
import wholock

try:
    os.replace("cache.db.tmp", "cache.db")
except PermissionError:
    raise RuntimeError(wholock.explain("cache.db"))
    # RuntimeError: cache.db is in use by antivirus.exe (pid 4242)

完整 API——四个函数、一个数据类:

import wholock

# 1) 查询:返回 Locker(pid, name, exe, kind, via, user, started) 列表
for p in wholock.who_locks("报告.xlsx"):
    print(p.pid, p.name, p.via)

# 2) 一句话解释:适合放进异常信息,文件不存在也不会抛错
msg = wholock.explain("报告.xlsx")

# 3) 等待释放:释放返回 True,超时返回 False
if wholock.wait_until_free("报告.xlsx", timeout=30):
    process_file("报告.xlsx")

# 4) 结束占用进程(带 PID 复用保护)
for locker, ok, message in wholock.kill_lockers("报告.xlsx"):
    print(locker.pid, ok, message)

典型用法:给 os.replace 加重试、Windows 测试 teardown 清理临时目录、 CI 里等待构建产物被释放、给安装器/更新器输出更有用的报错。

实现原理

系统 后端 说明
Windows Restart Manager API(ctypes 调 Rstrtmgr.dll) Windows 自家"文件被占用"对话框背后的官方机制,快、无竞态,多数场景不需要管理员
Linux 直接扫描 /proc 不依赖 lsof,能看到打开的 fd、内存映射、工作目录、可执行文件、chroot
macOS 系统自带 lsof 解析机器可读的 -F 输出

via 字段告诉你进程是怎么占用的:handle(Windows 句柄)、open(打开的 fd)、 mmap(内存映射)、cwd(它的工作目录)、exe(正在运行的程序本体)、root(chroot)。

杀进程的安全护栏:

  • 永远不会杀掉调用者自己和系统 PID(≤ 4)。
  • Windows/Linux 上先校验进程启动时间再杀,PID 被复用也不会误杀。
  • Windows 关键系统进程默认跳过,除非 --force
  • 僵尸进程视为已死(它们不占任何文件)。

已知限制(实话实说)

  • 未提权时可能看不到其他用户的进程。查不到结果且存在这种可能时, wholock 会提示你。
  • Windows 上查询目录时检查的是目录里的文件(上限 10,000 个)。 如果一个进程只是 cd 进了该文件夹,Restart Manager 看不到——文件夹删不掉时, 先找找开着它的资源管理器窗口或终端。
  • Windows Restart Manager 无法检查 ≥260 字符的路径(实测 \\?\ 前缀也无效)。 wholock 对这类文件会给出明确报错;目录里遇到超长文件则跳过并警告。
  • macOS 上扫描大目录较慢——那是 lsof +D 的特性。
  • 网络共享上的文件,系统能暴露多少就能查到多少。
  • Windows 的 --kill 永远是强制的(TerminateProcess),Windows 没有温和的跨进程 SIGTERM。

FAQ

为什么不用 psutil? psutil 是编译依赖;要找文件占用者得遍历每个进程的 open_files() (慢、常被权限挡、看不到内存映射),而且没有 CLI。 wholock 用操作系统官方推荐的方式直接问系统。

需要管理员/root 吗? 自己的进程通常不需要——"构建产物覆盖不了"、"Excel 还开着 CSV" 这类日常场景直接可用。系统服务和其他用户的进程可能需要提权才能看到。

能只关句柄不杀进程吗? 故意不支持。在活进程里强关句柄(Sysinternals Handle 的 -c)可能把那个进程 搞出更严重的状态损坏。杀掉或等待,两条路都是安全的。

参与贡献

欢迎提 issue 和 PR。测试矩阵覆盖 Windows / Linux / macOS × Python 3.9–3.14:

python -m unittest discover -s tests -t . -v

如果 wholock 救回了你的文件(或你的下午),点个 ⭐ 能帮更多人找到它。

许可证

MIT

About

Find which process is locking a file - and free it. Zero-dep CLI + Python API for Windows/Linux/macOS | 文件被占用无法删除?一条命令找出并结束占用进程 (WinError 32)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages