如何解决Dir.mktmpdir在Windows下无法删除临时目录的问题?
根因分析
这个报错是Windows系统的文件占用机制导致的,和目录本身的权限配置无关:
- Linux/macOS系统下,即使文件被进程打开,只要权限足够依然可以执行删除操作;但Windows会强制锁定被进程持有句柄的文件/目录,只要句柄没释放,删除操作会直接抛出权限拒绝错误
- 代码里执行
File.write、File.delete后,Ruby的文件IO句柄不会被即时回收,依赖GC机制异步释放。等Dir.mktmpdir块执行到末尾自动清理临时目录时,残留的句柄还没被系统释放,就会触发这个rmdir报错 - 另外原代码里的断言写法本身有问题:
assert_equal需要传入「期望值」「实际值」两个参数,只传一个参数会触发参数错误,判断文件存在应该用assert File.exist?(f)
修复方案
按优先级选择对应方案即可:
- 最小改动修复:在块末尾、临时目录被自动删除前,强制触发GC回收未释放的文件句柄
class TestFoo < Minitest::Test def test_basic Dir.mktmpdir 'test' do |dir| f = File.join(dir, 'foo.txt') File.write(f, "Hello") assert File.exist?(f) # 修正原断言的参数错误 File.delete(f) GC.start # 强制回收IO句柄,解决Windows下句柄延迟释放问题 end end end
- CI环境适配:从报错路径看是在GitHub Actions的Windows runner上跑测试,这类环境默认开启Windows Defender实时扫描,新建的临时文件会被杀毒软件短暂锁定,光触发GC不够,需要给目录删除逻辑加重试:
# 给FileUtils的删除逻辑加重试,遇到占用就等100ms再试,最多重试3次 original_remove_entry = FileUtils.method(:remove_entry) FileUtils.define_singleton_method(:remove_entry) do |path, force = false| remaining_retries = 3 begin original_remove_entry.call(path, force) rescue Errno::EACCES remaining_retries -= 1 sleep 0.1 retry if remaining_retries > 0 raise end end
- 源头减少句柄残留:所有文件操作优先用块形式调用,块退出时会立刻关闭文件句柄,比直接调用
File.write更稳妥:
# 替换原来的File.write调用 File.open(f, 'w') { |io| io.write("Hello") }
避坑提示
- 尽量不要把临时目录放在同步盘、杀毒软件重点监控的目录下,这类程序会频繁扫描新建文件,非常容易触发句柄占用
- 不要依赖Ruby的GC自动回收IO资源,涉及文件、网络连接的资源,优先用块形式调用确保即时释放。
内容的提问来源于stack exchange,提问作者yegor256
相关产品推荐
相关产品推荐

