递归删除目录和所有符号链接
Delete directory and all symlinks recursively
我试过用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'
快速搜索表明我 not alone 遇到了这个问题。
在我的理解中,rmtree
函数等同于 rm -Rf $DIR
shell 命令 - 但事实并非如此。
p.s。用于重建目的。请创建一个符号 link 例如使用 ln -s /path/to/original /path/to/link
来自How to remove a directory including all its files in 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)
那应该删除包含文件内容的目录。
这很奇怪,在 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!
您可能在 Mac OSX 上并且您的目录至少部分在非 Mac 文件系统(即不是 HFS+)上。在那些,Mac 文件系统驱动程序自动创建前缀为 ._
的二进制伴随文件来记录所谓的 扩展属性 (在 https://apple.stackexchange.com/questions/14980/why-are-dot-underscore-files-created-and-how-can-i-avoid-them 中解释,但也如下图所示)。
rmtree
在不支持 os.scandir
中的文件描述符的系统上(如 Mac OSX)现在会不安全地创建一个条目列表,然后通过一个(创建一个已知的竞争条件:https://github.com/python/cpython/blob/908fd691f96403a3c30d85c17dd74ed1f26a60fd/Lib/shutil.py#L592-L621)。不幸的是,两种不同的行为每次都使这个条件为真:
- 原始文件总是列在扩展属性前,并且
- 取消链接原始文件 (
test.txt
) 时,同时删除元文件 (._test.txt
)。
这样,轮到扩展属性文件就会丢失,抛出你遇到的FileNotFoundError
我认为这个错误最好由 cpython#14064 解决,它旨在忽略 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#PS
问题是自动取消链接原始文件也会取消链接配套文件。
/Volumes/Untitled/test $ rm test.txt
/Volumes/Untitled/test $ ls -a
. ..
我试过用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'
快速搜索表明我 not alone 遇到了这个问题。
在我的理解中,rmtree
函数等同于 rm -Rf $DIR
shell 命令 - 但事实并非如此。
p.s。用于重建目的。请创建一个符号 link 例如使用 ln -s /path/to/original /path/to/link
来自How to remove a directory including all its files in 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)
那应该删除包含文件内容的目录。
这很奇怪,在 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!
您可能在 Mac OSX 上并且您的目录至少部分在非 Mac 文件系统(即不是 HFS+)上。在那些,Mac 文件系统驱动程序自动创建前缀为 ._
的二进制伴随文件来记录所谓的 扩展属性 (在 https://apple.stackexchange.com/questions/14980/why-are-dot-underscore-files-created-and-how-can-i-avoid-them 中解释,但也如下图所示)。
rmtree
在不支持 os.scandir
中的文件描述符的系统上(如 Mac OSX)现在会不安全地创建一个条目列表,然后通过一个(创建一个已知的竞争条件:https://github.com/python/cpython/blob/908fd691f96403a3c30d85c17dd74ed1f26a60fd/Lib/shutil.py#L592-L621)。不幸的是,两种不同的行为每次都使这个条件为真:
- 原始文件总是列在扩展属性前,并且
- 取消链接原始文件 (
test.txt
) 时,同时删除元文件 (._test.txt
)。
这样,轮到扩展属性文件就会丢失,抛出你遇到的FileNotFoundError
我认为这个错误最好由 cpython#14064 解决,它旨在忽略 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#PS
问题是自动取消链接原始文件也会取消链接配套文件。
/Volumes/Untitled/test $ rm test.txt
/Volumes/Untitled/test $ ls -a
. ..