你可能正在使用 Mac OSX 操作系统,而且你的目录至少部分位于非 Mac 文件系统上(即非 HFS+)。在这些文件系统上,Mac 文件系统驱动程序会自动创建以
._
为前缀的二进制补充文件,记录所谓的
扩展属性(在
https://apple.stackexchange.com/questions/14980/why-are-dot-underscore-files-created-and-how-can-i-avoid-them中有解释,也在下面说明)。
对于不支持
os.scandir
中的文件描述符的系统(如 Mac OSX),
rmtree
现在不安全地创建一个条目列表,然后逐个删除它们(创建已知的竞争条件:
https://github.com/python/cpython/blob/908fd691f96403a3c30d85c17dd74ed1f26a60fd/Lib/shutil.py#L592-L621)。不幸的是,两种不同的行为使得这个条件每次都成立:
- 原始文件总是在扩展属性文件之前列出,并且
- 当删除原始文件(
test.txt
)时,元文件(._test.txt
)同时被删除。
因此,当扩展属性文件轮到它的时候会丢失,并抛出你遇到的
FileNotFoundError
。
我认为最好通过
cpython#14064来解决这个错误,该 PR 旨在一般忽略
rmtree
中的
FileNotFoundError
。
缓解方法
在此期间,你可以使用
onerror
忽略这些元文件上的删除错误:
def ignore_extended_attributes(func, filename, exc_info):
is_meta_file = os.path.basename(filename).startswith("._")
if not (func is os.unlink and is_meta_file):
raise
shutil.rmtree(path_dir, onerror=ignore_extended_attributes)
Mac扩展属性的示例
为了说明,您可以创建一个小型的ExFAT磁盘映像,并使用以下命令将其挂载到/Volumes/Untitled
:
hdiutil create -size 5m -fs exfat test.dmg
hdiutil attach test.dmg # mounts at /Volumes/Untitled
cd /Volumes/Untitled
mkdir test # create a directory to remove
cd test
touch test.txt
open test.txt # open the test.txt file in the standard editor
在标准文本编辑器中打开文件会创建一个扩展属性文件._test.txt
,并记录最后访问时间:
/Volumes/Untitled/test $ ls -a
. .. ._test.txt test.txt
/Volumes/Untitled/test $ xattr test.txt
com.apple.lastuseddate
问题在于自动取消链接原始文件也会取消链接伴随的文件。
/Volumes/Untitled/test $ rm test.txt
/Volumes/Untitled/test $ ls -a
. ..
path_dir
是一个符号链接的路径吗? - gimix