10.10. shutil —高级文件操作

源代码: Lib/shutil.py


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

Warning

甚至更高级别的文件复制Function(shutil.copy()shutil.copy2())也无法复制所有文件元数据。

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

10.10.1. 目录和文件操作

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

    • 将文件状对象* fsrc 的内容复制到文件状对象 fdst 。整数 length (如果给出)是缓冲区大小。特别地,负 length 值表示要复制数据而不会以块的形式遍历源数据。默认情况下,将以块的形式读取数据,以避免不受控制的内存消耗。请注意,如果 fsrc *对象的当前文件位置不为 0,则仅复制从当前文件位置到文件末尾的内容。
  • shutil. copyfile(* src dst *)

    • 将名为* src 的文件的内容(没有元数据)复制到名为 dst *的文件。 * dst 必须是完整的目标文件名;查看shutil.copy()以获取接受目标目录路径的副本。如果 src dst 是相同的文件,则引发Error。目标位置必须是可写的;否则,将引发IOError异常。如果 dst *已经存在,它将被替换。无法使用此Function复制特殊文件,例如字符或块设备以及管道。 * src dst *是以字符串形式给出的路径名。
  • shutil. copymode(* src dst *)

    • 将权限位从* src 复制到 dst *。文件内容,所有者和组不受影响。 * src dst *是以字符串形式给出的路径名。
  • shutil. copystat(* src dst *)

    • 将权限位,上次访问时间,上次修改时间和标志从* src 复制到 dst *。文件内容,所有者和组不受影响。 * src dst *是以字符串形式给出的路径名。
  • shutil. copy(* src dst *)

    • 将文件* src 复制到文件或目录 dst 。如果 dst 是目录,则在指定目录中创建(或覆盖)与 src *基本名称相同的文件。权限位被复制。 * src dst *是以字符串形式给出的路径名。
  • shutil. copy2(* src dst *)

    • copy()相同,除了copy2()还会try保留文件元数据。

copy2()使用copystat()复制文件元数据。有关更多信息,请参见copystat()

  • shutil. ignore_patterns(*模式)
    • 此工厂函数创建的函数可用作copytree()的* ignore 参数的可调用函数,而忽略与提供的 glob 样式 patterns *之一匹配的文件和目录。请参见下面的示例。

2.6 版的新Function。

  • shutil. copytree(* src dst symlinks = False ignore = None *)
    • 递归复制以* src 为根的整个目录树。用 dst *命名的目标目录必须不存在;它会被创建以及丢失的父目录。目录的权限和时间使用copystat()复制,单个文件使用shutil.copy2()复制。

如果* symlinks *为 true,则将源树中的符号链接表示为新树中的符号链接,但是不会复制原始链接的元数据。如果为 false 或Ellipsis,则将链接文件的内容和元数据复制到新树。

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

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

源代码应视为示例而非finally工具。

在版本 2.3 中进行了更改:如果在复制而不是打印消息期间发生任何异常,则会引发Error

在版本 2.5 中进行了更改:创建创建* dst *所需的中间目录,而不是引发错误。使用copystat()复制目录的权限和时间。

在 2.6 版中进行了更改:添加了* ignore *参数以能够影响所复制的内容。

  • shutil. rmtree(* path * [,* ignore_errors * [,* onerror *]])
    • 删除整个目录树; * path 必须指向目录(但不能指向目录的符号链接)。如果 ignore_errors 为 true,则删除失败导致的错误将被忽略;如果为 false 或被忽略,则pass调用 onerror *指定的处理程序来处理此类错误;或者,如果忽略该错误,则会引发异常。

如果提供了* onerror ,则它必须是可调用的,可以接受三个参数: function path excinfo 。第一个参数 function 是引发异常的函数;它将是os.path.islink()os.listdir()os.remove()os.rmdir()。第二个参数 path 将是传递给 function 的路径名。第三个参数 excinfo *将是sys.exc_info()返回的异常信息。 * onerror *引发的异常不会被捕获。

在 2.6 版中进行了更改:明确检查* path *是符号链接,并在这种情况下提高OSError

  • shutil. move(* src dst *)
    • 将文件或目录(* src )递归移动到另一个位置( dst *)。

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

如果目标位于当前文件系统上,则使用os.rename()。否则,将* src 复制(使用shutil.copy2())到 dst *,然后将其删除。

2.3 版的新Function。

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

2.3 版的新Function。

10.10.1.1. 复制树示例

此示例是上述copytree()函数的实现,其中Ellipsis了文档字符串。它演示了此模块提供的许多其他Function。

def copytree(src, dst, symlinks=False, ignore=None):
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        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, ignore)
            else:
                copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error) 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 WindowsError:
        # can't copy file access times on Windows
        pass
    except OSError as why:
        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)

10.10.2. 归档操作

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

  • shutil. make_archive(* base_name format * [,* root_dir * [,* base_dir * [,* verbose * [,* dry_run * [,* owner * [,* group * [,* logger *]]]]]] ]])
    • 创建一个存档文件(例如 zip 或 tar)并返回其名称。
  • base_name *是要创建的文件的名称,包括路径,减去任何特定于格式的 extensions。 * format *是存档格式:“ zip”(如果有zlib模块或外部zip可执行文件可用),“ tar”,“ gztar”(如果有zlib模块可用)或“ bztar”(如果有bz2模块可用)。

  • root_dir 是一个目录,它将是 Files 的根目录;即。我们通常在创建 Files 之前将 chdir 导入 root_dir *。

  • base_dir *是我们开始存档的目录;即。 * base_dir *将是存档中所有文件和目录的通用前缀。

  • root_dir base_dir *都默认为当前目录。

创建 tar 归档文件时使用* owner group *。默认情况下,使用当前所有者和组。

2.7 版的新Function。

  • shutil. get_archive_formats ( )
    • 返回用于归档的受支持格式的列表。返回序列的每个元素都是 Tuples(name, description)

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

    • zip *:ZIP 文件(如果有zlib模块或外部zip可执行文件)。
    • tar *:未压缩的 tar 文件。
    • gztar *:gzip 压缩的 tar 文件(如果zlib模块可用)。
    • bztar *:bzip2 版本的 tar 文件(如果bz2模块可用)。

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

2.7 版的新Function。

  • shutil. register_archive_format(* name function * [,* extra_args * [,* description *]])
    • 注册格式为* name *的存档器。 * function *是可调用的,将用于调用存档器。

如果给定,* extra_args *是(name, value)的序列,当使用可调用的 Archiver 时,它将用作额外的关键字参数。

2.7 版的新Function。

  • shutil. unregister_archive_format(* name *)
    • 从支持的格式列表中删除存档格式* name *。

2.7 版的新Function。

10.10.2.1. 存档示例

在此示例中,我们创建一个 gzip 压缩的 tar 文件 Files,其中包含在用户的.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