shutil — 高级文件操作 — Python 文档

来自菜鸟教程
Python/docs/3.9/library/shutil
跳转至:导航、​搜索

shutil — 高级文件操作

源代码: :source:`Lib/shutil.py`



shutil 模块提供了许多对文件和文件集合的高级操作。 特别是提供了支持文件复制和删除的功能。 对于单个文件的操作,另见 os 模块。

警告

即使是更高级别的文件复制功能(shutil.copy()shutil.copy2())也无法复制所有文件元数据。

在 POSIX 平台上,这意味着文件所有者和组以及 ACL 都将丢失。 在 Mac OS 上,不使用资源分支和其他元数据。 这意味着资源将丢失,文件类型和创建者代码将不正确。 在 Windows 上,不会复制文件所有者、ACL 和备用数据流。


目录和文件操作

shutil.copyfileobj(fsrc, fdst[, length])
将类文件对象 fsrc 的内容复制到类文件对象 fdst。 整数 length(如果给定)是缓冲区大小。 特别是,负 length 值意味着复制数据而不在块中循环遍历源数据; 默认情况下,数据是分块读取的,以避免不受控制的内存消耗。 注意,如果fsrc对象的当前文件位置不为0,则只会复制从当前文件位置到文件末尾的内容。
shutil.copyfile(src, dst, *, follow_symlinks=True)

将名为 src 的文件的内容(无元数据)复制到名为 dst 的文件中,并以尽可能最有效的方式返回 dstsrcdst 是类似路径的对象或以字符串形式给出的路径名。

dst 必须是完整的目标文件名; 查看 copy() 以获取接受目标目录路径的副本。 如果 srcdst 指定相同的文件,则引发 SameFileError

目标位置必须是可写的; 否则,将引发 OSError 异常。 如果 dst 已经存在,它将被替换。 使用此功能无法复制特殊文件,例如字符或块设备和管道。

如果 follow_symlinks 为 false 且 src 是符号链接,则将创建一个新的符号链接,而不是复制 src 指向的文件。

在 3.3 版更改:IOError 过去被引发而不是 OSError。 添加了 follow_symlinks 参数。 现在返回 dst

3.4 版更改: 引发 SameFileError 而不是 Error。 由于前者是后者的子类,因此此更改是向后兼容的。

3.8 版更改: 可以在内部使用特定于平台的快速复制系统调用,以便更有效地复制文件。 请参阅 依赖于平台的高效复制操作 部分。

exception shutil.SameFileError

如果 copyfile() 中的源和目标是同一个文件,则会引发此异常。

3.4 版中的新功能。

shutil.copymode(src, dst, *, follow_symlinks=True)

将权限位从 src 复制到 dst。 文件内容、所有者和组不受影响。 srcdst 是类似路径的对象或以字符串形式给出的路径名。 如果 follow_symlinks 为 false,并且 srcdst 都是符号链接,则 copymode() 将尝试修改 的模式]dst 本身(而不是它指向的文件)。 此功能并非在每个平台上都可用; 请参阅 copystat() 了解更多信息。 如果 copymode() 不能修改本地平台上的符号链接,并且被要求这样做,它什么也不做并返回。

3.3 版更改: 添加 follow_symlinks 参数。

shutil.copystat(src, dst, *, follow_symlinks=True)

将权限位、上次访问时间、上次修改时间和标志从 src 复制到 dst。 在 Linux 上,copystat() 还会在可能的情况下复制“扩展属性”。 文件内容、所有者和组不受影响。 srcdst 是类似路径的对象或以字符串形式给出的路径名。

如果 follow_symlinks 为 false,并且 srcdst 都引用符号链接,则 copystat() 将对符号链接本身进行操作,而不是而不是符号链接所指的文件——从 src 符号链接读取信息,并将信息写入 dst 符号链接。

笔记

并非所有平台都提供检查和修改符号链接的能力。 Python 本身可以告诉您哪些功能是本地可用的。

  • 如果 os.chmod in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的权限位。

  • 如果 os.utime in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的最后访问和修改次数。

  • 如果 os.chflags in os.supports_follow_symlinksTrue,则 copystat() 可以修改符号链接的标志。 (os.chflags 并非在所有平台上都可用。)

在部分或所有此功能不可用的平台上,当要求修改符号链接时,copystat() 将复制所有可以复制的内容。 copystat() 从不返回失败。

请参阅 os.supports_follow_symlinks 了解更多信息。

3.3 版更改: 添加 follow_symlinks 参数并支持 Linux 扩展属性。

shutil.copy(src, dst, *, follow_symlinks=True)

将文件src复制到文件或目录dstsrcdst 应该是 类路径对象 或字符串。 如果 dst 指定目录,则文件将使用 src 中的基本文件名复制到 dst。 返回新创建文件的路径。

如果 follow_symlinks 为 false,并且 src 是符号链接,则 dst 将被创建为符号链接。 如果 follow_symlinks 为真且 src 是符号链接,则 dst 将是 src 所指文件的副本。

copy() 复制文件数据和文件的权限模式(见 os.chmod())。 其他元数据,如文件的创建和修改时间,不会保留。 要保留原始文件中的所有文件元数据,请改用 copy2()

3.3 版更改: 添加 follow_symlinks 参数。 现在返回新创建文件的路径。

3.8 版更改: 可以在内部使用特定于平台的快速复制系统调用,以便更有效地复制文件。 请参阅 依赖于平台的高效复制操作 部分。

shutil.copy2(src, dst, *, follow_symlinks=True)

copy() 相同,除了 copy2() 也尝试保留文件元数据。

follow_symlinks 为 false,并且 src 是符号链接时,copy2() 尝试将所有元数据从 src 符号链接复制到新创建的 dst 符号链接。 但是,此功能并非在所有平台上都可用。 在部分或全部此功能不可用的平台上,copy2() 将保留所有可能的元数据; copy2() 永远不会引发异常,因为它无法保留文件元数据。

copy2() 使用 copystat() 复制文件元数据。 请参阅 copystat() 以获取有关修改符号链接元数据的平台支持的更多信息。

在 3.3 版更改: 添加 follow_symlinks 参数,也尝试复制扩展文件系统属性(目前仅适用于 Linux)。 现在返回新创建文件的路径。

3.8 版更改: 可以在内部使用特定于平台的快速复制系统调用,以便更有效地复制文件。 请参阅 依赖于平台的高效复制操作 部分。

shutil.ignore_patterns(*patterns)
此工厂函数创建一个函数,该函数可用作 copytree()' 的 ignore 参数的可调用函数,忽略与 glob 样式 之一匹配的文件和目录] 模式 提供。 请参阅下面的示例。
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)

递归复制以 src 为根的整个目录树到名为 dst 的目录并返回目标目录。 dirs_exist_ok 指示是否在 dst 或任何丢失的父目录已经存在的情况下引发异常。

使用copystat()复制目录的权限和时间,使用copy2()复制单个文件。

如果 symlinks 为真,则源树中的符号链接在新树中表示为符号链接,并且将在平台允许的范围内复制原始链接的元数据; 如果为 false 或省略,则链接文件的内容和元数据将复制到新树中。

symlinks 为 false 时,如果符号链接指向的文件不存在,则会在副本末尾的 Error 异常中引发的错误列表中添加异常过程。 如果要消除此异常,可以将可选的 ignore_dangling_symlinks 标志设置为 true。 请注意,此选项对不支持 os.symlink() 的平台无效。

如果给出 ignore,则它必须是一个可调用对象,它将接收 copytree() 访问的目录作为其参数,以及 返回的内容列表os.listdir()。 由于 copytree() 是递归调用的,因此每个复制的目录都会调用一次 ignore 可调用对象。 可调用对象必须返回相对于当前目录的目录和文件名序列(即 第二个参数中项目的子集); 这些名称将在复制过程中被忽略。 ignore_patterns() 可用于创建这样一个忽略基于 glob 样式模式的名称的可调用对象。

如果发生异常,则会引发 Error 并列出原因。

如果给出 copy_function,它必须是一个可调用的,用于复制每个文件。 它将以源路径和目标路径作为参数被调用。 默认情况下,使用 copy2(),但可以使用任何支持相同签名的函数(如 copy())。

在 3.3 版更改:symlinks 为 false 时复制元数据。 现在返回 dst

3.2 版更改: 添加 copy_function 参数以提供自定义复制功能。 当 symlinks 为 false 时,将 ignore_dangling_symlinks 参数添加到无声悬挂符号链接错误中。

3.8 版更改: 可以在内部使用特定于平台的快速复制系统调用,以便更有效地复制文件。 请参阅 依赖于平台的高效复制操作 部分。

3.8 新功能: dirs_exist_ok 参数。

shutil.rmtree(path, ignore_errors=False, onerror=None)

删除整个目录树; path 必须指向一个目录(但不是指向目录的符号链接)。 如果 ignore_errors 为真,则删除失败导致的错误将被忽略; 如果为 false 或省略,则通过调用由 onerror 指定的处理程序来处理此类错误,或者,如果省略,则引发异常。

笔记

在支持必要的基于 fd 的函数的平台上,默认使用 rmtree() 的符号链接攻击抗性版本。 在其他平台上,rmtree() 实现容易受到符号链接攻击:在适当的时间和情况下,攻击者可以操纵文件系统上的符号链接来删除他们无法以其他方式访问的文件。 应用程序可以使用 rmtree.avoids_symlink_attacks 函数属性来确定适用哪种情况。

如果提供了 onerror,它必须是一个接受三个参数的可调用对象:functionpathexcinfo

第一个参数 function 是引发异常的函数; 这取决于平台和实现。 第二个参数 path 将是传递给 function 的路径名。 第三个参数excinfosys.exc_info()返回的异常信息。 onerror 引发的异常不会被捕获。

在 3.3 版更改: 添加了一个抗符号链接攻击的版本,如果平台支持基于 fd 的功能,则会自动使用该版本。

在 3.8 版更改: 在 Windows 上,将不再在删除结点之前删除目录结点的内容。

rmtree.avoids_symlink_attacks

指示当前平台和实现是否提供了 rmtree() 的符号链接攻击抗性版本。 目前这仅适用于支持基于 fd 的目录访问功能的平台。

3.3 版中的新功能。

shutil.move(src, dst, copy_function=copy2)

递归地将文件或目录 (src) 移动到另一个位置 (dst) 并返回目的地。

如果目标是现有目录,则将 src 移动到该目录中。 如果目标已经存在但不是目录,则可能会根据 os.rename() 语义被覆盖。

如果目标在当前文件系统上,则使用 os.rename()。 否则,使用 copy_functionsrc 复制到 dst,然后删除。 在符号链接的情况下,一个指向 src 目标的新符号链接将被创建或作为 dstsrc 将被删除。

如果给出 copy_function,它必须是一个带有两个参数 srcdst 的可调用对象,并将用于将 src 复制到dst 如果 os.rename() 不能使用。 如果源是目录,则调用 copytree(),将 copy_function() 传递给它。 默认 copy_functioncopy2()。 使用 copy() 作为 copy_function 允许在不可能也复制元数据时移动成功,代价是不复制任何元数据。

3.3 版更改: 为外部文件系统添加了显式符号链接处理,从而使其适应 GNU 的 mv 的行为。 现在返回 dst

3.5 版更改: 添加 copy_function 关键字参数。

3.8 版更改: 可以在内部使用特定于平台的快速复制系统调用,以便更有效地复制文件。 请参阅 依赖于平台的高效复制操作 部分。

3.9 版更改: 接受 srcdst类路径对象

shutil.disk_usage(path)

返回给定路径的磁盘使用统计信息作为 命名元组 具有属性 totalusedfree,这是数量总空间、已用空间和可用空间,以字节为单位。 path 可以是文件或目录。

3.3 版中的新功能。

在 3.8 版更改: 在 Windows 上,path 现在可以是文件或目录。

shutil.chown(path, user=None, group=None)

更改给定 路径 的所有者 用户 和/或

user 可以是系统用户名或uid; 这同样适用于 。 至少需要一个参数。

另见 os.chown(),底层函数。

3.3 版中的新功能。

shutil.which(cmd, mode=os.F_OK | os.X_OK, path=None)

如果给定的 cmd 被调用,则返回可执行文件的路径。 如果不会调用 cmd,则返回 None

mode 是传递给 os.access() 的权限掩码,默认确定文件是否存在和可执行。

如果未指定 path,则使用 os.environ() 的结果,返回“PATH”值或 os.defpath 的回退。

在 Windows 上,当前目录总是预先添加到 path 中,无论您是使用默认值还是提供您自己的,这是命令外壳在查找可执行文件时使用的行为。 另外,当在path中找到cmd时,会检查PATHEXT环境变量。 例如,如果您调用 shutil.which("python")which() 将搜索 PATHEXT 以知道它应该在 path[ 中查找 python.exe X136X] 目录。 例如,在 Windows 上:

>>> shutil.which("python")
'C:\\Python33\\python.EXE'

3.3 版中的新功能。

在 3.8 版更改:现在接受 bytes 类型。 如果 cmd 类型为 bytes,则结果类型也为 bytes

exception shutil.Error
此异常收集在多文件操作期间引发的异常。 对于 copytree(),异常参数是一个 3 元组列表(srcname, dstname, exception)。

依赖平台的高效复制操作

从 Python 3.8 开始,所有涉及文件复制的函数 (copyfile(), copy(), copy2(), copytree()[X133X ] 和 move()) 可以使用特定于平台的“快速复制”系统调用来更有效地复制文件(参见 :issue:`33671`)。 “快速复制”意味着复制操作发生在内核中,避免在 Python 中使用用户空间缓冲区,如“outfd.write(infd.read())”。

在 macOS 上 fcopyfile 用于复制文件内容(不是元数据)。

在 Linux 上使用 os.sendfile()

在 Windows 上 shutil.copyfile() 使用更大的默认缓冲区大小(1 MiB 而不是 64 KiB)和基于 memoryview()shutil.copyfileobj() 变体使用

如果快速复制操作失败并且目标文件中没有写入数据,那么shutil 将在内部静默地使用效率较低的copyfileobj() 函数。

在 3.8 版中更改。


复制树示例

这个例子是上面描述的 copytree() 函数的实现,省略了文档字符串。 它演示了该模块提供的许多其他功能。

def copytree(src, dst, symlinks=False):
    names = os.listdir(src)
    os.makedirs(dst)
    errors = []
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks)
            else:
                copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except OSError as why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error as err:
            errors.extend(err.args[0])
    try:
        copystat(src, dst)
    except OSError as why:
        # can't copy file access times on Windows
        if why.winerror is None:
            errors.extend((src, dst, str(why)))
    if errors:
        raise Error(errors)

另一个使用 ignore_patterns() 助手的示例:

from shutil import copytree, ignore_patterns

copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))

这将复制除 .pyc 文件和名称以 tmp 开头的文件或目录之外的所有内容。

另一个使用 ignore 参数添加日志调用的示例:

from shutil import copytree
import logging

def _logpath(path, names):
    logging.info('Working in %s', path)
    return []   # nothing will be ignored

copytree(source, destination, ignore=_logpath)

rmtree 示例

此示例显示如何在 Windows 上删除目录树,其中某些文件设置了只读位。 它使用 onerror 回调来清除只读位并重新尝试删除。 任何后续故障都会传播。

import os, stat
import shutil

def remove_readonly(func, path, _):
    "Clear the readonly bit and reattempt the removal"
    os.chmod(path, stat.S_IWRITE)
    func(path)

shutil.rmtree(directory, onerror=remove_readonly)

归档操作

3.2 版中的新功能。


3.5 版更改: 增加了对 xztar 格式的支持。


还提供了用于创建和读取压缩和存档文件的高级实用程序。 它们依赖于 zipfiletarfile 模块。

shutil.make_archive(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])

创建存档文件(例如 zip 或 tar)并返回其名称。

base_name 是要创建的文件的名称,包括路径,减去任何特定于格式的扩展名。 format 是存档格式:“zip”(如果 zlib 模块可用)、“tar”、“gztar”(如果 zlib 模块)之一可用)、“bztar”(如果 bz2 模块可用)或“xztar”(如果 lzma 模块可用)。

root_dir 是一个目录,它将成为存档的根目录,存档中的所有路径都将相对于它; 例如,我们通常在创建存档之前 chdir 进入 root_dir

base_dir 是我们开始归档的目录; IE base_dir 将是存档中所有文件和目录的公共前缀。 base_dir 必须相对于 root_dir。 有关如何一起使用 base_dirroot_dir,请参阅 使用 base_dir 的存档示例。

root_dirbase_dir 都默认为当前目录。

如果 dry_run 为真,则不创建存档,但将执行的操作记录到 logger

创建 tar 存档时使用 ownergroup。 默认情况下,使用当前所有者和组。

logger 必须是与 PEP 282 兼容的对象,通常是 logging.Logger 的实例。

verbose 参数未使用且已弃用。

笔记

此函数不是线程安全的。

在 3.8 版中更改:现在使用现代 pax (POSIX.1-2001) 格式代替旧的 GNU 格式用于使用 format="tar" 创建的档案。

shutil.get_archive_formats()

返回支持的归档格式列表。 返回序列的每个元素都是一个元组 (name, description)

默认情况下 shutil 提供以下格式:

  • zip:ZIP 文件(如果 zlib 模块可用)。

  • tar:未压缩的 tar 文件。 对新档案使用 POSIX.1-2001 pax 格式。

  • gztar:gzip 压缩的 tar 文件(如果 zlib 模块可用)。

  • bztar:bzip2 压缩的 tar 文件(如果 bz2 模块可用)。

  • xztar:xz 压缩的 tar 文件(如果 lzma 模块可用)。

您可以使用 register_archive_format() 注册新格式或为任何现有格式提供您自己的存档器。

shutil.register_archive_format(name, function[, extra_args[, description]])

name 格式注册存档器。

function 是用于解压档案的可调用函数。 callable 将接收要创建的文件的 base_name,然后是 base_dir(默认为 os.curdir)以开始存档。 进一步的参数作为关键字参数传递: ownergroupdry_runlogger(在 make_archive()[ X152X])。

如果给定,extra_args(name, value) 对的序列,当使用存档器可调用时,这些对将用作额外的关键字参数。

descriptionget_archive_formats() 使用,它返回存档器列表。 默认为空字符串。

shutil.unregister_archive_format(name)
从支持的格式列表中删除存档格式 name
shutil.unpack_archive(filename[, extract_dir[, format]])

解压存档。 filename 是存档的完整路径。

extract_dir 是解压存档的目标目录的名称。 如果未提供,则使用当前工作目录。

format 是存档格式:“zip”、“tar”、“gztar”、“bztar”或“xztar”之一。 或使用 register_unpack_format() 注册的任何其他格式。 如果未提供,unpack_archive() 将使用存档文件扩展名并查看是否为该扩展名注册了解包程序。 如果没有找到,则会引发 ValueError

3.7 版更改: 接受 文件名extract_dir类路径对象

shutil.register_unpack_format(name, extensions, function[, extra_args[, description]])

注册解包格式。 name 是格式的名称,而 extensions 是与该格式对应的扩展名列表,例如 Zip 文件的 .zip

function 是用于解压档案的可调用函数。 callable 将接收存档的路径,然后是存档必须解压缩到的目录。

提供时, extra_args 是一个 (name, value) 元组序列,这些元组将作为关键字参数传递给可调用对象。

description 可用于描述格式,并由 get_unpack_formats() 函数返回。

shutil.unregister_unpack_format(name)
取消注册解包格式。 name 是格式的名称。
shutil.get_unpack_formats()

返回所有已注册格式的列表以进行解包。 返回序列的每个元素都是一个元组 (name, extensions, description)

默认情况下 shutil 提供以下格式:

  • zip:ZIP 文件(解压压缩文件仅在相应模块可用时才有效)。

  • tar:未压缩的 tar 文件。

  • gztar:gzip 压缩的 tar 文件(如果 zlib 模块可用)。

  • bztar:bzip2 压缩的 tar 文件(如果 bz2 模块可用)。

  • xztar:xz 压缩的 tar 文件(如果 lzma 模块可用)。

您可以使用 register_unpack_format() 注册新格式或为任何现有格式提供自己的解包器。

归档示例

在此示例中,我们创建了一个 gzip 压缩的 tar 文件存档,其中包含在用户的 .ssh 目录中找到的所有文件:

>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> root_dir = os.path.expanduser(os.path.join('~', '.ssh'))
>>> make_archive(archive_name, 'gztar', root_dir)
'/Users/tarek/myarchive.tar.gz'

生成的存档包含:

$ tar -tzvf /Users/tarek/myarchive.tar.gz
drwx------ tarek/staff       0 2010-02-01 16:23:40 ./
-rw-r--r-- tarek/staff     609 2008-06-09 13:26:54 ./authorized_keys
-rwxr-xr-x tarek/staff      65 2008-06-09 13:26:54 ./config
-rwx------ tarek/staff     668 2008-06-09 13:26:54 ./id_dsa
-rwxr-xr-x tarek/staff     609 2008-06-09 13:26:54 ./id_dsa.pub
-rw------- tarek/staff    1675 2008-06-09 13:26:54 ./id_rsa
-rw-r--r-- tarek/staff     397 2008-06-09 13:26:54 ./id_rsa.pub
-rw-r--r-- tarek/staff   37192 2010-02-06 18:23:10 ./known_hosts

使用 base_dir 归档示例

在这个例子中,类似于上面的,我们展示了如何使用make_archive(),但这次使用的是base_dir。 我们现在有以下目录结构:

$ tree tmp
tmp
└── root
    └── structure
        ├── content
            └── please_add.txt
        └── do_not_add.txt

在最终存档中,应包含 please_add.txt,但不应包含 do_not_add.txt。 因此,我们使用以下内容:

>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> make_archive(
...     archive_name,
...     'tar',
...     root_dir='tmp/root',
...     base_dir='structure/content',
... )
'/Users/tarek/my_archive.tar'

列出生成的存档中的文件给我们:

$ python -m tarfile -l /Users/tarek/myarchive.tar
structure/content/
structure/content/please_add.txt

查询输出终端的大小

shutil.get_terminal_size(fallback=(columns, lines))

获取终端窗口的大小。

对于两个维度中的每一个,分别检查环境变量 COLUMNSLINES。 如果变量已定义且值为正整数,则使用它。

当没有定义COLUMNSLINES时,这是常见的情况,通过调用os.get_terminal_size()[查询连接到sys.__stdout__的终端X169X]。

如果无法成功查询终端大小,要么是因为系统不支持查询,要么是因为我们没有连接到终端,则使用 fallback 参数中给出的值。 fallback 默认为 (80, 24),这是许多终端模拟器使用的默认大小。

返回的值是一个 os.terminal_size 类型的命名元组。

另请参阅:单一 UNIX 规范,版本 2, 其他环境变量

3.3 版中的新功能。