递归删除目录和所有符号链接

问题描述 投票:0回答:3

我尝试使用

shutil
删除目录及其所有包含的文件,如下:

import shutil
from os.path import exists
if exists(path_dir):
    shutil.rmtree(path_dir)

不幸的是,我的解决方案不起作用,抛出以下错误:

FileNotFoundError: [Errno 2] No such file or directory: '._image1.jpg'

快速搜索表明我不是唯一一个遇到这个问题的人。 根据我的理解, rmtree

 函数相当于 
rm -Rf $DIR
 shell 命令 - 但事实似乎并非如此。

附注用于重建目的。请创建一个符号链接,例如使用

ln -s /path/to/original /path/to/link


    

python recursion directory symlink delete-file
3个回答
5
投票
这很奇怪,在 Windows 10 和 Ubuntu 20.04.2 LTS 中,我对要删除的文件夹下有或没有符号链接的 Shutil.rmtree() 没有任何问题。

无论如何,请尝试以下代码。我在 Windows 10 和 Ubuntu 中尝试过。

from pathlib import Path import shutil def delete_dir_recursion(p): """ Delete folder, sub-folders and files. """ for f in p.glob('**/*'): if f.is_symlink(): f.unlink(missing_ok=True) # missing_ok is added in python 3.8 print(f'symlink {f.name} from path {f} was deleted') elif f.is_file(): f.unlink() print(f'file: {f.name} from path {f} was deleted') elif f.is_dir(): try: f.rmdir() # delete empty sub-folder print(f'folder: {f.name} from path {f} was deleted') except OSError: # sub-folder is not empty delete_dir_recursion(f) # recurse the current sub-folder except Exception as exception: # capture other exception print(f'exception name: {exception.__class__.__name__}') print(f'exception msg: {exception}') try: p.rmdir() # time to delete an empty folder print(f'folder: {p.name} from path {p} was deleted') except NotADirectoryError: p.unlink() # delete folder even if it is a symlink, linux print(f'symlink folder: {p.name} from path {p} was deleted') except Exception as exception: print(f'exception name: {exception.__class__.__name__}') print(f'exception msg: {exception}') def delete_dir(folder): p = Path(folder) if not p.exists(): print(f'The path {p} does not exists!') return # Attempt to delete the whole folder at once. try: shutil.rmtree(p) except Exception as exception: print(f'exception name: {exception.__class__.__name__}') print(f'exception msg: {exception}') # continue parsing the folder else: # else if no issues on rmtree() if not p.exists(): # verify print(f'folder {p} was successfully deleted by shutil.rmtree!') return print(f'Parse the folder {folder} ...') delete_dir_recursion(p) if not p.exists(): # verify print(f'folder {p} was successfully deleted!') # start folder_to_delete = '/home/zz/tmp/sample/b' # delete folder b delete_dir(folder_to_delete)
输出示例:

我们要删除文件夹

b

. ├── 1.txt ├── a ├── b │   ├── 1 │   ├── 1.txt -> ../1.txt │   ├── 2 │   │   └── 21 │   │   └── 21.txt │   ├── 3 │   │   └── 31 │   ├── 4 │   │   └── c -> ../../c │   ├── a -> ../a │   └── b.txt ├── c
Parse the folder /home/zz/tmp/sample/b ...
symlink a from path /home/zz/tmp/sample/b/a was deleted
symlink c from path /home/zz/tmp/sample/b/4/c was deleted
folder: 4 from path /home/zz/tmp/sample/b/4 was deleted
symlink 1.txt from path /home/zz/tmp/sample/b/1.txt was deleted
file: b.txt from path /home/zz/tmp/sample/b/b.txt was deleted
file: 21.txt from path /home/zz/tmp/sample/b/2/21/21.txt was deleted
folder: 21 from path /home/zz/tmp/sample/b/2/21 was deleted
folder: 2 from path /home/zz/tmp/sample/b/2 was deleted
folder: 1 from path /home/zz/tmp/sample/b/1 was deleted
folder: 31 from path /home/zz/tmp/sample/b/3/31 was deleted
folder: 3 from path /home/zz/tmp/sample/b/3 was deleted
folder: b from path /home/zz/tmp/sample/b was deleted
folder /home/zz/tmp/sample/b was successfully deleted!
    

3
投票

更新:底层 python 错误已由 cpython#14064 修复,这将成为 python 3.13 的一部分(下面早期版本的缓解代码)。

您可能使用的是 Mac OSX,并且您的目录至少部分位于非 Mac 文件系统上(即不是 HFS+)。在这些文件系统上,Mac 文件系统驱动程序会自动创建前缀为

._

 的二进制配套文件,以记录所谓的 
扩展属性(在 https://apple.stackexchange.com/questions/14980/why-are-dot-underscore 中进行解释) -文件创建和如何避免它们,但也在下面说明)。

rmtree

 在不支持 
os.scandir
 中的文件描述符的系统(如 Mac OSX)上,现在会不安全地创建一个条目列表,然后逐一取消它们的链接(创建一个已知的竞争条件:
https://github.com)。 com/python/cpython/blob/908fd691f96403a3c30d85c17dd74ed1f26a60fd/Lib/shutil.py#L592-L621)。不幸的是,两个单独的行为每次都使这个条件成立:

    原始文件始终列在扩展属性之前,并且
  1. 当原始文件取消链接 (
  2. test.txt
    ) 时,元文件 (
    ._test.txt
    ) 将同时删除。
这样,轮到的时候扩展属性文件就会丢失,抛出你现在遇到的

FileNotFoundError

我认为这个错误最好由

cpython#14064 解决,其目的是一般忽略 FileNotFoundError

 中的 
rmtree

缓解措施

同时,您可以使用

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#PS
问题是自动取消链接原始文件也会取消链接伴随文件。

/Volumes/Untitled/test $ rm test.txt /Volumes/Untitled/test $ ls -a . ..
    

1
投票
来自

如何在 python 中删除包含其所有文件的目录?

# function that deletes all files and then folder import glob, os def del_folder(dir_name): dir_path = os.getcwd() + "\{}".format(dir_name) try: os.rmdir(dir_path) # remove the folder except: print("OSError") # couldn't remove the folder because we have files inside it finally: # now iterate through files in that folder and delete them one by one and delete the folder at the end try: for filepath in os.listdir(dir_path): os.remove(dir_path + "\{}".format(filepath)) os.rmdir(dir_path) print("folder is deleted") except: print("folder is not there")
您也可以将 

ignore_errors

 标志与 
shutil.rmtree() 一起使用。

shutil.rmtree('/folder_name', ignore_errors=True)

这应该删除包含文件内容的目录。

© www.soinside.com 2019 - 2024. All rights reserved.