我有一个功能可以删除空文件夹,直到找到包含内容的文件夹。问题是,当删除子项时,循环迭代速度太快,当使用 listdir
检查父项是否为空时,子项仍然会弹出。 (虽然我还没有尝试过)我怀疑当我在删除调用后添加
sleep(0.1)
时,它会起作用。但这并不是问题的正确解决方案。需要有一种方法可以等到文件夹完全删除后再继续。该功能(简化)可以在下面找到。当调用 rmdir
函数时,我们循环回到顶部并到达
listdir
,上一次迭代中删除的文件夹有时仍然会弹出。def delete_empty_folders(top_folder: str, root_folder: str) -> None:
"""Keep deleting empty folders until we reach a folder with content
or the root folder"""
while True:
if not exists(top_folder):
top_folder = dirname(top_folder)
continue
if samefile(top_folder, root_folder):
break
if listdir(top_folder):
break
rmdir(top_folder)
top_folder = dirname(top_folder)
return
这个
问题在于,在某些设置(操作系统、文件系统、网络挂载与否等)中,文件和文件夹会被异步删除。这意味着文件夹“提交删除”和实际删除之间存在一段时间。此时,我的算法已经再次迭代并到达
listdir
,在那里弹出所谓的已删除文件夹(因为它已被
标记删除,但实际上尚未被删除) . Python 中无法等到异步删除指令完成。我用以下(简化的)算法替代版本修复了它:
from os import listdir
from os.path import exists, join, samefile
from shutil import rmtree
def delete_empty_folders(top_folder: str, root_folder: str) -> None:
"""Keep deleting empty folders until we reach a folder with content
or the root folder"""
parent_folder = top_folder
child_folder = None
while parent_folder:
if exists(parent_folder):
if samefile(parent_folder, root_folder):
break
if listdir(parent_folder) not in ([], [child_folder]):
# Folder has content and it's not only the empty child
break
child_folder = basename(parent_folder)
parent_folder = dirname(parent_folder)
lowest_empty_folder = join(parent_folder, child_folder)
rmtree(lowest_empty_folder, ignore_errors=True)
return
此版本不会单独删除每个文件夹。它立即搜索最低的文件夹,然后递归地将它们全部删除。对于变化较少的版本,请参见下文。这里我们只是稍后检查一下该文件夹是否同时没有被删除。
from os import listdir, rmdir
from os.path import exists, samefile
from time import sleep
def delete_empty_folders(top_folder: str, root_folder: str) -> None:
"""Keep deleting empty folders until we reach a folder with content
or the root folder"""
while True:
if not exists(top_folder):
top_folder = dirname(top_folder)
continue
if samefile(top_folder, root_folder):
break
if listdir(top_folder):
# Check in 200ms if the folder still exists.
# Could be that because of async folder deletion,
# the folder is submitted for deletion but not
# yet actually deleted.
# Increase value for more safety.
sleep(0.2)
if listdir(top_folder):
break
rmdir(top_folder)
top_folder = dirname(top_folder)
return