Skip to main content

11.10. shutil —高级文件操作

源代码: Lib/shutil.py


shutil 模块提供了对文件和文件集合的一些高级操作。特别地,提供了支持文件复制和移除的功能。有关单个文件的操作,另请参见 os 模块。

警告

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

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

11.10.1. 目录和文件操作

shutil.copyfileobj(fsrc, fdst[, length])

将类文件对象 fsrc 的内容复制到类文件对象 fdst。整数 length (如果给出)是缓冲区大小。具体地,负 length 值意味着复制数据而不使块中的源数据循环;默认情况下,以块为单位读取数据,以避免不受控制的内存消耗。请注意,如果 fsrc 对象的当前文件位置不为0,则只会复制当前文件位置到文件结尾的内容。

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

将名为 src 的文件的内容(无元数据)复制到名为 dst 的文件,并返回 dstsrcdst 是以字符串形式给出的路径名。 dst 必须是完整的目标文件名;请查看 shutil.copy() 以获取接受目标目录路径的副本。如果 srcdst 指定相同的文件,则引发 SameFileError

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

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

在 3.3 版更改: IOError 以前是升高而不是 OSError。添加了 follow_symlinks 参数。现在返回 dst

在 3.4 版更改: 提高 SameFileError,而不是 Error。因为前者是后者的子类,所以这种变化是向后兼容的。

exception shutil.SameFileError

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

3.4 新版功能.

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

将权限位从 src 复制到 dst。文件内容,所有者和组不受影响。 srcdst 是以字符串形式给出的路径名。如果 follow_symlinks 为假,并且 srcdst 都是符号链接,copymode() 将尝试修改 dst 本身的模式(而不是它指向的文件)。此功能不适用于每个平台;请参阅 copystat() 了解更多信息。如果 copymode() 无法修改本地平台上的符号链接,并且要求这样做,它将不会执行任何操作并返回。

在 3.3 版更改: 添加了 follow_symlinks 参数。

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

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

如果 follow_symlinks 为假,并且 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 复制到文件或目录 dst 中。 srcdst 应为字符串。如果 dst 指定目录,则文件将使用 src 的基本文件名复制到 dst 中。返回新创建的文件的路径。

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

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

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

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

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

follow_symlinks 为假,且 src 为符号链接时,copy2() 尝试将所有元数据从 src 符号链接复制到新创建的 dst 符号链接。但是,此功能不适用于所有平台。在其中某些或所有功能不可用的平台上,copy2() 将保留其可用的所有元数据; copy2() 永远不会返回失败。

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

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

shutil.ignore_patterns(*patterns)

这个工厂函数创建一个函数,该函数可以用作 copytree()ignore 参数的可调用函数,忽略与提供的glob-style patterns 匹配的文件和目录。请参见下面的示例。

shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False)

递归地复制以 src 为根的整个目录树,返回目标目录。由 dst 命名的目标目录不能已经存在;它将被创建以及缺少父目录。使用 copystat() 复制目录的权限和时间,使用 shutil.copy2() 复制单个文件。

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

symlinks 为假时,如果符号链接指向的文件不存在,则在复制过程结束时在 Error 异常中引发的错误列表中将添加一个异常。如果要静默此异常,可以将可选的 ignore_dangling_symlinks 标志设置为true。请注意,此选项对不支持 os.symlink() 的平台没有影响。

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

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

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

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

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

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

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

注解

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

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

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

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

指示当前平台和实现是否提供符合连接攻击的 rmtree() 版本。目前,这只适用于支持基于fd的目录访问功能的平台。

3.3 新版功能.

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

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

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

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

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

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

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

shutil.disk_usage(path)

将有关给定路径的磁盘使用统计信息作为具有属性 totalusedfreenamed tuple,这些属性是总的,已用的和可用空间的量(以字节为单位)。

3.3 新版功能.

可用性:Unix,Windows。

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

更改给定 path 的所有者 user 和/或 group

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

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

可用性:Unix。

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 前面,无论是使用默认还是提供自己的,这是命令shell在查找可执行文件时使用的行为。此外,当在 path 中找到 cmd 时,检查 PATHEXT 环境变量。例如,如果你调用 shutil.which("python")which() 将搜索 PATHEXT 知道它应该在 path 目录中查找 python.exe。例如,在Windows上:

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

3.3 新版功能.

exception shutil.Error

此异常收集在多文件操作期间引发的异常。对于 copytree(),异常参数是3元组(srcnamedstnameexception)的列表。

11.10.1.1. copytree示例

此示例是上述 copytree() 函数的实现,省略了docstring。它演示了此模块提供的许多其他功能。

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)

11.10.1.2. rmtree示例

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

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)

11.10.2. 存档操作

3.2 新版功能.

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

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

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

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

root_dir 是将成为归档的根目录的目录;例如,我们通常在创建存档之前将chdir导入 root_dir

base_dir 是我们开始归档的目录;即 base_dir 将是归档中所有文件和目录的公共前缀。

root_dirbase_dir 都默认为当前目录。

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

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

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

verbose 参数未使用并已弃用。

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

shutil.get_archive_formats()

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

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

  • gztar:gzip的tar文件

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

  • xztar:xz’ed tar文件(如果 lzma 模块可用)。

  • tar:未压缩的tar文件

  • zip:ZIP文件

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

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

name 格式注册存档程序。

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

如果给出,extra_args 是一个 (name, value) 对序列,当使用archiver callable时,将用作额外的关键字参数。

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

shutil.unregister_archive_format(name)

从支持的格式列表中删除归档格式 name

shutil.unpack_archive(filename[, extract_dir[, format]])

解压缩归档。 filename 是归档的完整路径。

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

format 是归档格式:“zip”,“tar”或“gztar”之一。或任何其他格式注册与 register_unpack_format()。如果没有提供,unpack_archive() 将使用存档文件扩展名,并查看是否为该扩展注册了解包程序。在没有发现的情况下,产生 ValueError

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

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

function 是将用于解压缩归档的可调用。可调用程序将接收归档的路径,然后是归档必须提取到的目录。

当提供时,extra_args 是将作为关键字参数传递给可调用的 (name, value) 元组的序列。

可以提供 description 来描述格式,并且将由 get_unpack_formats() 函数返回。

shutil.unregister_unpack_format(name)

取消注册解包格式。 name 是格式的名称。

shutil.get_unpack_formats()

返回所有注册格式的解压缩列表。返回的序列的每个元素是元组 (name, extensions, description)

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

  • gztar:gzip的tar文件

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

  • xztar:xz’ed tar文件(如果 lzma 模块可用)。

  • tar:未压缩的tar文件

  • zip:ZIP文件

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

11.10.2.1. 归档示例

在本示例中,我们创建一个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

11.10.3. 查询输出端子的大小

3.3 新版功能.

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

获取终端窗口的大小。

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

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

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

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

参见:单UNIX规范,版本2,Other Environment Variables