考虑下面的Python函数来清理目录:
def cleanDir(path):
shutil.rmtree(path)
os.mkdir(path)
在Windows上(使用python 2.7.10和3.4.4在Windows7和Windows10上进行了实际测试),当使用Windows资源管理器同时导航到相应的目录时(或仅在左侧树窗格中导航到父文件夹时),可能会引发以下异常:
Traceback (most recent call last):
...
File "cleanDir.py", line ..., in cleanDir
os.mkdir(path)
PermissionError: [WinError 5] Access is denied: 'testFolder'
这个问题已在本报告中报告。但是没有进行进一步的分析,并且给出的使用睡眠的解决方案并不令人满意。根据Eryk下面的评论,在当前python版本(即python 3.8)之前,同样的行为也是可以预期的
注意shutil.rmtree
毫无例外地返回。但立即尝试再次创建目录可能会失败。(重试在大多数情况下都是成功的,请参阅下面的测试完整代码。)请注意,您需要在Windows资源管理器的测试文件夹(左侧和右侧)中单击以强制解决问题
问题似乎出在Windows文件系统API函数中(而不是Python os
模块中):当Windows资源管理器对相应的文件夹具有句柄时,删除的文件夹似乎不会立即“转发”到所有函数
import os, shutil
import time
def populateFolder(path):
if os.path.exists(path):
with open(os.path.join(path,'somefile.txt'), 'w') as f:
f.write('test')
#subfolderpath = os.path.join(path,'subfolder')
#os.mkdir(subfolderpath)
#with open(os.path.join(subfolderpath,'anotherfile.txt'), 'w') as f2:
# f2.write('test')
def cleanDir(path):
shutil.rmtree(path)
os.mkdir(path)
def cleanDir_safe(path):
shutil.rmtree(path)
try:
#time.sleep(0.005) # makes first try of os.mkdir successful
os.mkdir(path)
except Exception as e:
print('os.mkdir failed: %s' % e)
time.sleep(0.01)
os.mkdir(path)
assert os.path.exists(path)
FOLDER_PATH = 'testFolder'
if os.path.exists(FOLDER_PATH):
cleanDir(FOLDER_PATH)
else:
os.mkdir(FOLDER_PATH)
loopCnt = 0
while True:
populateFolder(FOLDER_PATH)
#cleanDir(FOLDER_PATH)
cleanDir_safe(FOLDER_PATH)
time.sleep(0.01)
loopCnt += 1
if loopCnt % 100 == 0:
print(loopCnt)
资源管理器具有共享删除/重命名访问权限的目录的打开句柄。这允许
rmdir
成功,而正常情况下,open不会共享删除/重命名访问,并且rmdir
会因共享冲突而失败(32)。但是,即使rmdir
成功,在资源管理器关闭其句柄之前,目录实际上不会解除链接。它正在监视目录的更改,因此会收到目录已被删除的通知,但即使它立即关闭其句柄,脚本的os.mkdir
调用也会出现竞争条件您应该在循环中重试
os.mkdir
,超时时间会增加。您还需要一个onerror
处理程序来处理shutil.rmtree
试图删除的目录不是空的,因为它包含“已删除”的文件或目录例如:
讨论
在常见情况下,可以“避免”此问题,因为现有打开不共享删除/重命名访问权限。在这种情况下,尝试删除文件或目录失败,共享冲突(winerror 32)。例如,如果某个目录作为进程的工作目录打开,则它不共享删除/重命名访问权限。对于常规文件,大多数程序只共享读/执行和写/附加访问
临时文件通常使用“删除/重命名访问共享”打开,尤其是使用“删除/重命名”访问打开临时文件时(例如,使用“关闭时删除”标志打开)。这是导致“已删除”文件仍然链接但无法访问的最常见原因。另一种情况是打开目录以监视更改(例如,请参见^{} )。通常,此打开将共享删除/重命名访问权限,这是本问题中Explorer的情况
对于Unix开发人员来说,声明文件被删除而没有取消链接可能听起来很奇怪(至少可以这么说)。在Windows中,删除文件(或目录)只是在其文件控制块(FCB)上设置删除配置。当文件系统清除文件的最后一个内核文件对象引用时,设置了delete处置集的文件将自动取消链接。文件对象通常由
CreateFileW
创建,它返回对象的句柄。当文件对象的最后一个句柄关闭时,会触发文件对象的清理。由于子进程中的句柄继承或显式DuplicateHandle
调用,文件对象可能存在多个句柄引用重申一下,一个文件或目录可能被多个内核文件对象引用,每个内核文件对象可能被多个句柄引用。通常,使用经典的Windows删除语义,在文件解除链接之前,必须关闭所有句柄。此外,设置delete处置并不一定是最终的。如果任何打开的句柄具有删除/重命名访问权限,则实际上可以通过清除删除处置(例如,请参见^{} :
FileDispositionInfo
)来恢复对文件的访问权限在Windows 10中,内核还支持POSIX delete语义,即一旦删除句柄关闭,文件或目录就会立即取消链接(请参阅NTAPI^{} 的详细信息)。NTFS已更新为支持POSIX删除语义。最近,WINAPI
DeleteFileW
(即Pythonos.remove
)已切换到在文件系统支持的情况下使用它,但RemoveDirectoryW
(即Pythonos.rmdir
)仍限于经典的Windows删除对于NTFS来说,实现POSIX语义相对容易。它只需设置删除配置并将文件重命名为NTFS保留目录“\$Extend\$Deleted”,其名称基于其文件ID。实际上,该文件似乎已取消链接,同时继续允许现有文件对象访问该文件。与传统删除相比,一个显著的区别是原始名称丢失,因此具有删除/重命名访问权限的现有句柄无法取消删除处置
我重命名文件夹并删除“新建”文件夹,然后创建(预期)文件夹
相关问题 更多 >
编程相关推荐