创建临时文件、日志或构建工件的脚本通常需要自行清理。您可能需要删除单个文件、匹配特定模式的所有文件或整个目录树。

Python 没有单一函数来完成所有这些操作。os、pathlib 和 shutil 模块各自覆盖该任务的不同部分。

本文介绍如何使用 os.remove()、pathlib.Path.unlink() 和 shutil.rmtree() 删除文件和目录,包括基于模式的删除和递归目录删除。

删除文件

在 Python 中,您可以使用 os.remove()、os.unlink()、pathlib.Path.unlink() 删除单个文件。

os 模块提供了一种与操作系统交互的可移植方式。

要使用 os.remove() 删除单个文件,请将文件路径作为参数传递:

import os

file_path = '/tmp/file.txt'
os.remove(file_path)

os.remove() 和 os.unlink() 函数在语义上是相同的:

import os

file_path = '/tmp/file.txt'
os.unlink(file_path)

如果指定的文件不存在,Python 会抛出 FileNotFoundError 异常。os.remove() 和 os.unlink() 只能删除文件,不能删除目录。如果给定的路径指向一个目录,Python 会抛出 OSError;具体的子类取决于操作系统。

删除文件需要包含该文件的目录具有写入和执行权限。否则,您将收到 PermissionError。

为避免删除文件时出错,您可以使用异常处理来捕获异常并发送适当的错误消息:

import os

file_path = '/tmp/file.txt'

try:
    os.remove(file_path)
except OSError as e:
    print("Error: %s: %s" % (file_path, e.strerror))

pathlib 模块提供了用于处理文件系统路径的面向对象的接口。它在 Python 3.4 及以上版本中可用。

要使用 pathlib 模块删除文件,请创建一个指向该文件的 Path 对象,并在该对象上调用 unlink() 方法:

from pathlib import Path

file_path = Path('/tmp/file.txt')

try:
    file_path.unlink()
except OSError as e:
    print("Error: %s: %s" % (file_path, e.strerror))

在 Python 3.8 及以上版本中,您可以传递 missing_ok=True 来抑制文件不存在时的 FileNotFoundError:

from pathlib import Path

Path('/tmp/file.txt').unlink(missing_ok=True)

pathlib.Path.unlink()、os.remove() 和 os.unlink() 也可用于删除符号链接。

如果文件存在则删除文件

常见需求是仅在文件存在时删除它,如果不存在则不抛出错误。如果您使用的是 Python 3.8 或更新版本,Path.unlink(missing_ok=True) 是最简单的选项,如上所示。

对于 os.remove(),建议捕获 FileNotFoundError 而不是先进行检查。这避免了另一种进程在检查后但在 os.remove() 运行之前删除文件的竞争条件:

import os

file_path = '/tmp/file.txt'

try:
    os.remove(file_path)
except FileNotFoundError:
    pass

您也可能看到使用 os.path.exists() 先检查路径的示例:

import os

file_path = '/tmp/file.txt'

if os.path.exists(file_path):
    os.remove(file_path)

这对于简单脚本来说是可以接受的,但它不如处理 FileNotFoundError 安全。它对于损坏的符号链接也返回 False;如果您需要检测并删除损坏的符号链接,请使用 os.path.lexists()。

按模式删除文件

您可以使用 glob 模块根据模式匹配多个文件。例如,要删除 /tmp 目录中的所有 .txt 文件,您可以使用类似以下内容:

import os
import glob

files = glob.glob('/tmp/*.txt')

for f in files:
    try:
        os.remove(f)
    except OSError as e:
        print("Error: %s: %s" % (f, e.strerror))

要递归删除 /tmp 目录及其所有子目录中的所有 .txt 文件,请将 recursive=True 参数传递给 glob() 函数并使用 ** 模式:

import os
import glob

files = glob.glob('/tmp/**/*.txt', recursive=True)

for f in files:
    try:
        os.remove(f)
    except OSError as e:
        print("Error: %s: %s" % (f, e.strerror))

pathlib 模块包含两个 glob 方法 glob() 和 rglob(),用于匹配目录中的路径。glob() 遵循您提供的相对模式,包括嵌套和递归模式。rglob() 在不需要 **/ 前缀的情况下递归搜索。以下示例删除 /tmp 目录顶层中的所有 .txt 文件:

from pathlib import Path

for f in Path('/tmp').glob('*.txt'):
    try:
        f.unlink()
    except OSError as e:
        print("Error: %s: %s" % (f, e.strerror))

删除目录中的所有文件

有时您需要清空目录但保留目录本身。例如,您可能希望在脚本每次运行之前清除缓存或上传文件夹。

警告使用 unlink() 删除的文件不会进入回收站。先打印路径列表并在删除任何内容之前进行检查。

要查看目录中的条目,请使用 iterdir() 对其进行循环并打印每个路径:

from pathlib import Path

for f in Path('/tmp/data').iterdir():
    print(f)

iterdir() 方法返回目录中的每个条目,包括隐藏文件和子目录。以下代码删除文件和符号链接并保留子目录:

from pathlib import Path

for f in Path('/tmp/data').iterdir():
    if f.is_file() or f.is_symlink():
        f.unlink()

is_file() 方法遵循符号链接,当链接目标缺失时返回 False。is_symlink() 检查可捕获这些损坏的链接。指向目录的符号链接作为链接删除,而它指向的目录不受影响。

要同时删除所有子目录中的文件,请将 iterdir() 替换为 rglob('*')。实际的目录保留在原处,而文件和符号链接被删除:

from pathlib import Path

for f in Path('/tmp/data').rglob('*'):
    if f.is_file() or f.is_symlink():
        f.unlink()

您也可能看到使用 shutil.rmtree() 删除整个目录,然后使用 os.mkdir() 再次创建它的代码。它更短,但它也会删除子目录。新目录还会获得默认权限并属于运行脚本的用户,而不是原始所有者。

删除目录(文件夹)

在 Python 中,您可以使用 os.rmdir() 和 pathlib.Path.rmdir() 删除空目录,并使用 shutil.rmtree() 删除非空目录。

以下示例显示如何删除空目录:

import os

dir_path = '/tmp/img'

try:
    os.rmdir(dir_path)
except OSError as e:
    print("Error: %s: %s" % (dir_path, e.strerror))

或者,您可以使用 pathlib 模块删除目录:

from pathlib import Path

dir_path = Path('/tmp/img')

try:
    dir_path.rmdir()
except OSError as e:
    print("Error: %s: %s" % (dir_path, e.strerror))

shutil 模块允许您对文件和目录执行多种高级操作。

警告请小心使用 shutil.rmtree(),因为它会递归删除目录及其下的所有内容。运行之前仔细检查路径,特别是当路径来自变量或用户输入时。

使用 shutil.rmtree() 函数可以删除给定目录及其内容:

import shutil

dir_path = '/tmp/img'

try:
    shutil.rmtree(dir_path)
except OSError as e:
    print("Error: %s: %s" % (dir_path, e.strerror))

传递给 shutil.rmtree() 的参数不能是目录的符号链接。

要静默忽略删除期间的任何错误,请传递 ignore_errors=True:

import shutil

shutil.rmtree('/tmp/img', ignore_errors=True)

pathlib 模块没有用于删除非空目录的方法。Path.rmdir() 仅适用于空目录。要删除目录树,请将 Path 对象传递给 shutil.rmtree():

import shutil
from pathlib import Path

shutil.rmtree(Path('/tmp/img'))

快速参考

对于可打印的快速参考,请参阅 Python 速查表。

任务函数
删除文件os.remove(path) 或 Path(path).unlink()
删除文件(如果不存在则不报错)Path(path).unlink(missing_ok=True)
删除符号链接os.remove(path) 或 Path(path).unlink()
删除空目录os.rmdir(path) 或 Path(path).rmdir()
删除目录及其内容shutil.rmtree(path)
删除目录并忽略错误shutil.rmtree(path, ignore_errors=True)
按模式删除文件glob.glob(pattern) + os.remove()
递归按模式删除文件glob.glob('**/*.ext', recursive=True) + os.remove()
使用 pathlib glob 删除文件Path(dir).glob('*.ext') + .unlink()
递归使用 pathlib 删除文件Path(dir).rglob('*.ext') + .unlink()
删除目录中的所有文件Path(dir).iterdir() + 对文件调用 .unlink()

常见问题解答

os.remove() 和 os.unlink() 有什么区别?
它们在语义上是完全相同的。unlink 是该操作在 Unix 系统中的传统名称。两者都用于删除单个文件,如果路径不存在则会抛出 FileNotFoundError。

如何删除目录及其所有内容?
使用 shutil.rmtree(path)。若要抑制错误,请传递 ignore_errors=True。请注意,os.rmdir() 仅适用于空目录。

我可以使用 pathlib 删除匹配特定模式的文件吗?
可以。使用 Path(directory).glob('*.txt') 来匹配文件,并对每个结果调用 .unlink()。对于递归匹配,请使用 .rglob('*.txt')。