"tarfile" --- 读写tar归档文件
*****************************

**原始碼：**Lib/tarfile.py

======================================================================

"tarfile" 模块可以用来读写 tar 归档，包括使用 gzip, bz2 和 lzma 压缩的
归档。 请使用 "zipfile" 模块来读写 ".zip" 文件，或者使用 shutil 的高层
级函数。

一些事实和数字:

* 读写 "gzip", "bz2" 和 "lzma" 解压的归档要求相应的模块可用。

* 支持读取 / 写入 POSIX.1-1988 (ustar) 格式。

* 对 GNU tar 格式的读/写支持，包括 *longname* 和 *longlink* 扩展，对所
  有种类 *sparse* 扩展的只读支持，包括 sparse 文件的恢复。

* 对 POSIX.1-2001 (pax) 格式的读/写支持。

* 处理目录、正常文件、硬链接、符号链接、fifo 管道、字符设备和块设备，
  并且能够获取和恢复文件信息例如时间戳、访问权限和所有者等。

3.3 版更變: 添加了对 "lzma" 压缩的支持。

tarfile.open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)

   针对路径名 *name* 返回 "TarFile" 对象。 有关 "TarFile" 对象以及所允
   许的关键字参数的详细信息请参阅 TarFile 物件。

   *mode* 必须是 "'filemode[:compression]'" 形式的字符串，其默认值为
   "'r'"。 以下是模式组合的完整列表:

   +--------------------+-----------------------------------------------+
   | 模式               | action                                        |
   |====================|===============================================|
   | "'r' 或 'r:*'"     | 打开和读取使用透明压缩（推荐）。              |
   +--------------------+-----------------------------------------------+
   | "'r:'"             | 打开和读取不使用压缩。                        |
   +--------------------+-----------------------------------------------+
   | "'r:gz'"           | 打开和读取使用gzip 压缩。                     |
   +--------------------+-----------------------------------------------+
   | "'r:bz2'"          | 打开和读取使用bzip2 压缩。                    |
   +--------------------+-----------------------------------------------+
   | "'r:xz'"           | 打开和读取使用lzma 压缩。                     |
   +--------------------+-----------------------------------------------+
   | "'x'" 或 "'x:'"    | 创建tarfile不进行压缩。如果文件已经存在，则抛 |
   |                    | 出 "FileExistsError" 异常。                   |
   +--------------------+-----------------------------------------------+
   | "'x:gz'"           | 使用gzip压缩创建tarfile。如果文件已经存在，则 |
   |                    | 抛出 "FileExistsError" 异常。                 |
   +--------------------+-----------------------------------------------+
   | "'x:bz2'"          | 使用bzip2 压缩创建tarfile。如果文件已经存在， |
   |                    | 则抛出 "FileExistsError" 异常。               |
   +--------------------+-----------------------------------------------+
   | "'x:xz'"           | 使用lzma 压缩创建tarfile。如果文件已经存在，  |
   |                    | 则抛出 "FileExistsError" 异常。               |
   +--------------------+-----------------------------------------------+
   | "'a' 或 'a:'"      | 打开以便在没有压缩的情况下追加。如果文件不存  |
   |                    | 在，则创建该文件。                            |
   +--------------------+-----------------------------------------------+
   | "'w' 或 'w:'"      | 打开用于未压缩的写入。                        |
   +--------------------+-----------------------------------------------+
   | "'w:gz'"           | 打开用于 gzip 压缩的写入。                    |
   +--------------------+-----------------------------------------------+
   | "'w:bz2'"          | 打开用于 bzip2 压缩的写入。                   |
   +--------------------+-----------------------------------------------+
   | "'w:xz'"           | 打开用于 lzma 压缩的写入。                    |
   +--------------------+-----------------------------------------------+

   请注意 "'a:gz'", "'a:bz2'" 或 "'a:xz'" 是不可能的组合。 如果 *mode*
   不适用于打开特定（压缩的）文件用于读取，则会引发 "ReadError"。 请使
   用 *mode* "'r'" 来避免这种情况。 如果某种压缩方法不受支持，则会引发
   "CompressionError"。

   如果指定了 *fileobj*，它会被用作对应于 *name* 的以二进制模式打开的
   *file object* 的替代。 它会被设定为处在位置 0。

   对于 "'w:gz'", "'r:gz'", "'w:bz2'", "'r:bz2'", "'x:gz'", "'x:bz2'"
   等模式，"tarfile.open()" 接受关键字参数 *compresslevel* (默认值为
   "9") 来指定文件的压缩等级。

   对于 "'w:xz'" 和 "'x:xz'" 模式，"tarfile.open()" 接受关键字参数
   *preset* 来指定文件的压缩等级。

   针对特殊的目的，还存在第二种 *mode* 格式:
   "'filemode|[compression]'"。 "tarfile.open()" 将返回一个将其数据作
   为数据块流来处理的 "TarFile" 对象。 对此文件将不能执行随机查找。 如
   果给定了 *fileobj*，它可以是任何具有 "read()" 或 "write()" 方法 (由
   *mode* 确定) 的对象。 *bufsize* 指定块大小，默认值为 "20 * 512" 字
   节。 可与此格式组合使用的有 "sys.stdin", 套接字 *file object* 或磁
   带设备等。 但是，对于这样的 "TarFile" 对象存在不允许随机访问的限制
   ，参见 範例。 目前可用的模式如下:

   +---------------+----------------------------------------------+
   | 模式          | 动作                                         |
   |===============|==============================================|
   | "'r|*'"       | 打开 tar 块的 *流* 以进行透明压缩读取。      |
   +---------------+----------------------------------------------+
   | "'r|'"        | 打开一个未压缩的 tar 块的 *stream* 用于读取  |
   |               | 。                                           |
   +---------------+----------------------------------------------+
   | "'r|gz'"      | 打开一个 gzip 压缩的 *stream* 用于读取。     |
   +---------------+----------------------------------------------+
   | "'r|bz2'"     | 打开一个 bzip2 压缩的 *stream* 用于读取。    |
   +---------------+----------------------------------------------+
   | "'r|xz'"      | 打开一个 lzma 压缩 *stream* 用于读取。       |
   +---------------+----------------------------------------------+
   | "'w|'"        | 打开一个未压缩的 *stream* 用于写入。         |
   +---------------+----------------------------------------------+
   | "'w|gz'"      | 打开一个 gzip 压缩的 *stream* 用于写入。     |
   +---------------+----------------------------------------------+
   | "'w|bz2'"     | 打开一个 bzip2 压缩的 *stream* 用于写入。    |
   +---------------+----------------------------------------------+
   | "'w|xz'"      | 打开一个 lzma 压缩的 *stream* 用于写入。     |
   +---------------+----------------------------------------------+

   3.5 版更變: 添加了 "'x'" (单独创建) 模式。

   3.6 版更變: *name* 形参接受一个 *path-like object*。

class tarfile.TarFile

   用于读取和写入 tar 归档的类。 请不要直接使用这个类：而要使用
   "tarfile.open()"。 参见 TarFile 物件。

tarfile.is_tarfile(name)

   如果 *name* 是一个 "tarfile" 能读取的 tar 归档文件则返回 "True"。
   *name* 可以为 "str"，文件或文件类对象。

   3.9 版更變: 支持文件或类文件对象。

"tarfile" 模块定义了以下异常:

exception tarfile.TarError

   所有 "tarfile" 异常的基类。

exception tarfile.ReadError

   当一个不能被 "tarfile" 模块处理或者因某种原因而无效的 tar 归档被打
   开时将被引发。

exception tarfile.CompressionError

   当一个压缩方法不受支持或者当数据无法被正确解码时将被引发。

exception tarfile.StreamError

   当达到流式 "TarFile" 对象的典型限制时将被引发。

exception tarfile.ExtractError

   当使用 "TarFile.extract()" 时针对 *non-fatal* 所引发的异常，但是仅
   限 "TarFile.errorlevel""== 2"。

exception tarfile.HeaderError

   如果获取的缓冲区无效则会由 "TarInfo.frombuf()" 引发的异常。

exception tarfile.FilterError

   Base class for members refused by filters.

   tarinfo

      Information about the member that the filter refused to extract,
      as TarInfo.

exception tarfile.AbsolutePathError

   Raised to refuse extracting a member with an absolute path.

exception tarfile.OutsideDestinationError

   Raised to refuse extracting a member outside the destination
   directory.

exception tarfile.SpecialFileError

   Raised to refuse extracting a special file (e.g. a device or pipe).

exception tarfile.AbsoluteLinkError

   Raised to refuse extracting a symbolic link with an absolute path.

exception tarfile.LinkOutsideDestinationError

   Raised to refuse extracting a symbolic link pointing outside the
   destination directory.

以下常量在模块层级上可用:

tarfile.ENCODING

   默认的字符编码格式：在 Windows 上为 "'utf-8'"，其他系统上则为
   "sys.getfilesystemencoding()" 所返回的值。

以下常量各自定义了一个 "tarfile" 模块能够创建的 tar 归档格式。 相关细
节请参阅 受支持的 tar 格式 小节。

tarfile.USTAR_FORMAT

   POSIX.1-1988 (ustar) 格式。

tarfile.GNU_FORMAT

   GNU tar 格式。

tarfile.PAX_FORMAT

   POSIX.1-2001 (pax) 格式。

tarfile.DEFAULT_FORMAT

   用于创建归档的默认格式。 目前为 "PAX_FORMAT"。

   3.8 版更變: 新归档的默认格式已更改为 "PAX_FORMAT" 而不再是
   "GNU_FORMAT"。

也參考:

  "zipfile" 模組
     "zipfile" 标准模块的文档。

  归档操作
     标准 "shutil" 模块所提供的高层级归档工具的文档。

  GNU tar manual, Basic Tar Format
     针对 tar 归档文件的文档，包含 GNU tar 扩展。


TarFile 物件
============

"TarFile" 对象提供了一个 tar 归档的接口。 一个 tar 归档就是数据块的序
列。 一个归档成员（被保存文件）是由一个标头块加多个数据块组成的。 一个
文件可以在一个 tar 归档中多次被保存。 每个归档成员都由一个 "TarInfo"
对象来代表，详情参见 TarInfo 物件。

"TarFile" 对象可在 "with" 语句中作为上下文管理器使用。 当语句块结束时
它将自动被关闭。 请注意在发生异常事件时被打开用于写入的归档将不会被终
结；只有内部使用的文件对象将被关闭。 相关用例请参见 範例。

3.2 版新加入: 添加了对上下文管理器协议的支持。

class tarfile.TarFile(name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, dereference=False, ignore_zeros=False, encoding=ENCODING, errors='surrogateescape', pax_headers=None, debug=0, errorlevel=1)

   下列所有参数都是可选项并且也可作为实例属性来访问。

   *name* 是归档的路径名称。 *name* 可以是一个 *path-like object*。 如
   果给定了 *fileobj* 则它可以被省略。 在此情况下，如果对象的 "name"
   属性存在则它会被使用。

   *mode* 可以为 "'r'" 表示从现有归档读取，"'a'" 表示将数据追加到现有
   文件，"'w'" 表示创建新文件覆盖现有文件，或者 "'x'" 表示仅在文件不存
   在时创建新文件。

   如果给定了 *fileobj*，它会被用于读取或写入数据。 如果可以被确定，则
   *mode* 会被 *fileobj* 的模式所覆盖。 *fileobj* 的使用将从位置 0 开
   始。

   備註:

     当 "TarFile" 被关闭时，*fileobj* 不会被关闭。

   *format* 控制用于写入的归档格式。 它必须为在模块层级定义的常量
   "USTAR_FORMAT", "GNU_FORMAT" 或 "PAX_FORMAT" 中的一个。 当读取时，
   格式将被自动检测，即使单个归档中存在不同的格式。

   *tarinfo* 参数可以被用来将默认的 "TarInfo" 类替换为另一个。

   如果 *dereference* 为 "False"，则会将符号链接和硬链接添加到归档中。
   如果为 "True"，则会将目标文件的内容添加到归档中。 在不支持符号链接
   的系统上参数将不起作用。

   如果 *ignore_zeros* 为 "False"，则会将空的数据块当作归档的末尾来处
   理。 如果为 "True"，则会跳过空的（和无效的）数据块并尝试获取尽可能
   多的成员。 此参数仅适用于读取拼接的或损坏的归档。

   *debug* 可设为从 "0" (无调试消息) 到 "3" (全部调试消息)。 消息会被
   写入到 "sys.stderr"。

   *errorlevel* controls how extraction errors are handled, see "the
   corresponding attribute".

   *encoding* 和 *errors* 参数定义了读取或写入归档所使用的字符编码格式
   以及要如何处理转换错误。 默认设置将适用于大多数用户。 要深入了解详
   情可参阅 Unicode 问题 小节。

   可选的 *pax_headers* 参数是字符串的字典，如果 *format* 为
   "PAX_FORMAT" 它将被作为 pax 全局标头被添加。

   3.2 版更變: 使用 "'surrogateescape'" 作为 *errors* 参数的默认值。

   3.5 版更變: 添加了 "'x'" (单独创建) 模式。

   3.6 版更變: *name* 形参接受一个 *path-like object*。

classmethod TarFile.open(...)

   作为替代的构造器。 "tarfile.open()" 函数实际上是这个类方法的快捷方
   式。

TarFile.getmember(name)

   返回成员 *name* 的 "TarInfo" 对象。 如果 *name* 在归档中找不到，则
   会引发 "KeyError"。

   備註:

     如果一个成员在归档中出现超过一次，它的最后一次出现会被视为是最新
     的版本。

TarFile.getmembers()

   以 "TarInfo" 对象列表的形式返回归档的成员。 列表的顺序与归档中成员
   的顺序一致。

TarFile.getnames()

   以名称列表的形式返回成员。 它的顺序与 "getmembers()" 所返回列表的顺
   序一致。

TarFile.list(verbose=True, *, members=None)

   将内容清单打印到 "sys.stdout"。 如果 *verbose* 为 "False"，则将只打
   印成员名称。 如果为 "True"，则输出将类似于 **ls -l** 的输出效果。
   如果给定了可选的 *members*，它必须为 "getmembers()" 所返回的列表的
   一个子集。

   3.5 版更變: 新增 *members* 參數。

TarFile.next()

   当 "TarFile" 被打开用于读取时，以 "TarInfo" 对象的形式返回归档的下
   一个成员。 如果不再有可用对象则返回 "None"。

TarFile.extractall(path='.', members=None, *, numeric_owner=False, filter=None)

   将归档中的所有成员提取到当前工作目录或 *path* 目录。 如果给定了可选
   的 *members*，则它必须为 "getmembers()" 所返回的列表的一个子集。 字
   典信息例如所有者、修改时间和权限会在所有成员提取完毕后被设置。 这样
   做是为了避免两个问题：目录的修改时间会在每当在其中创建文件时被重置
   。 并且如果目录的权限不允许写入，提取文件到目录的操作将失败。

   如果 *numeric_owner* 为 "True"，则将使用来自 tarfile 的 uid 和 gid
   数值来设置被提取文件的所有者/用户组。 在其他情况下，则会使用来自
   tarfile 的名称值。

   The *filter* argument, which was added in Python 3.10.12, specifies
   how "members" are modified or rejected before extraction. See
   Extraction filters for details. It is recommended to set this
   explicitly depending on which *tar* features you need to support.

   警告:

     绝不要未经预先检验就从不可靠的源中提取归档文件。 这样有可能在
     *path* 之外创建文件，例如某些成员具有以 ""/"" 开始的绝对路径文件
     名或带有两个点号 "".."" 的文件名。Set "filter='data'" to prevent
     the most dangerous security issues, and read the Extraction
     filters section for details.

   3.5 版更變: 新增 *numeric_owner* 參數。

   3.6 版更變: *path* 形参接受一个 *path-like object*。

   3.10.12 版更變: 新增 *filter* 參數。

TarFile.extract(member, path='', set_attrs=True, *, numeric_owner=False, filter=None)

   从归档中提取出一个成员放入当前工作目录，将使用其完整名称。 成员的文
   件信息会尽可能精确地被提取。 *member* 可以是一个文件名或 "TarInfo"
   对象。 你可以使用 *path* 指定一个不同的目录。 *path* 可以是一个
   *path-like object*。 将会设置文件属性 (owner, mtime, mode) 除非
   *set_attrs* 为假值。

   The *numeric_owner* and *filter* arguments are the same as for
   "extractall()".

   備註:

     "extract()" 方法不会处理某些提取问题。 在大多数情况下你应当考虑使
     用 "extractall()" 方法。

   警告:

     參閱 "extractall()" 的警告。Set "filter='data'" to prevent the
     most dangerous security issues, and read the Extraction filters
     section for details.

   3.2 版更變: 增加 *set_attrs* 參數。

   3.5 版更變: 新增 *numeric_owner* 參數。

   3.6 版更變: *path* 形参接受一个 *path-like object*。

   3.10.12 版更變: 新增 *filter* 參數。

TarFile.extractfile(member)

   将归档中的一个成员提取为文件对象。 *member* 可以是一个文件名或
   "TarInfo" 对象。 如果 *member* 是一个常规文件或链接，则会返回一个
   "io.BufferedReader" 对象。 对于所有其他现有成员，则都将返回 "None"
   。 如果 *member* 未在归档中出现，则会引发 "KeyError"。

   3.3 版更變: 返回一个 "io.BufferedReader" 对象。

TarFile.errorlevel: int

   If *errorlevel* is "0", errors are ignored when using
   "TarFile.extract()" and "TarFile.extractall()". Nevertheless, they
   appear as error messages in the debug output when *debug* is
   greater than 0. If "1" (the default), all *fatal* errors are raised
   as "OSError" or "FilterError" exceptions. If "2", all *non-fatal*
   errors are raised as "TarError" exceptions as well.

   Some exceptions, e.g. ones caused by wrong argument types or data
   corruption, are always raised.

   Custom extraction filters should raise "FilterError" for *fatal*
   errors and "ExtractError" for *non-fatal* ones.

   Note that when an exception is raised, the archive may be partially
   extracted. It is the user’s responsibility to clean up.

TarFile.extraction_filter

   3.10.12 版新加入.

   The extraction filter used as a default for the *filter* argument
   of "extract()" and "extractall()".

   The attribute may be "None" or a callable. String names are not
   allowed for this attribute, unlike the *filter* argument to
   "extract()".

   If "extraction_filter" is "None" (the default), calling an
   extraction method without a *filter* argument will use the
   "fully_trusted" filter for compatibility with previous Python
   versions.

   In Python 3.12+, leaving "extraction_filter=None" will emit a
   "DeprecationWarning".

   In Python 3.14+, leaving "extraction_filter=None" will cause
   extraction methods to use the "data" filter by default.

   The attribute may be set on instances or overridden in subclasses.
   It also is possible to set it on the "TarFile" class itself to set
   a global default, although, since it affects all uses of *tarfile*,
   it is best practice to only do so in top-level applications or
   "site configuration". To set a global default this way, a filter
   function needs to be wrapped in "staticmethod()" to prevent
   injection of a "self" argument.

TarFile.add(name, arcname=None, recursive=True, *, filter=None)

   将文件 *name* 添加到归档。 *name* 可以为任意类型的文件（目录、fifo
   、符号链接等等）。 如果给出 *arcname* 则它将为归档中的文件指定一个
   替代名称。 默认情况下会递归地添加目录。 这可以通过将 *recursive* 设
   为 "False" 来避免。 递归操作会按排序顺序添加条目。 如果给定了
   *filter*，它应当为一个接受 "TarInfo" 对象并返回已修改 "TarInfo" 对
   象的函数。 如果它返回 "None" 则 "TarInfo" 对象将从归档中被排除。 具
   体示例参见 範例。

   3.2 版更變: 新增 *filter* 參數。

   3.7 版更變: 递归操作按排序顺序添加条目。

TarFile.addfile(tarinfo, fileobj=None)

   将 "TarInfo" 对象 *tarinfo* 添加到归档。 如果给定了 *fileobj*，它应
   当是一个 *binary file*，并会从中读取 "tarinfo.size" 个字节添加到归
   档。 你可以直接创建 "TarInfo" 对象，或是使用 "gettarinfo()" 来创建
   。

TarFile.gettarinfo(name=None, arcname=None, fileobj=None)

   基于 "os.stat()" 的结果或者现有文件的相同数据创建一个 "TarInfo"。
   文件或者是命名为 *name*，或者是使用文件描述符指定为一个 *file
   object* *fileobj*。 *name* 可以是一个 *path-like object*。 如果给定
   了 *arcname*，则它将为归档中的文件指定一个替代名称，在其他情况下，
   名称将从 *fileobj* 的 "name" 属性或 *name* 参数获取。 名称应当是一
   个文本字符串。

   你可以在使用 "addfile()" 添加 "TarInfo" 的某些属性之前修改它们。 如
   果文件对象不是从文件开头进行定位的普通文件对象，"size" 之类的属性就
   可能需要修改。 例如 "GzipFile" 之类的文件就属于这种情况。  "name"
   也可以被修改，在这种情况下 *arcname* 可以是一个占位字符串。

   3.6 版更變: *name* 形参接受一个 *path-like object*。

TarFile.close()

   关闭 "TarFile"。 在写入模式下，会向归档添加两个表示结束的零数据块。

TarFile.pax_headers

   一个包含 pax 全局标头的键值对的字典。


TarInfo 物件
============

"TarInfo" 对象代表 "TarFile" 中的一个文件。 除了会存储所有必要的文件属
性（例如文件类型、大小、时间、权限、所有者等），它还提供了一些确定文件
类型的有用方法。 此对象 *并不* 包含文件数据本身。

"TarInfo" objects are returned by "TarFile"'s methods "getmember()",
"getmembers()" and "gettarinfo()".

Modifying the objects returned by "getmember()" or "getmembers()" will
affect all subsequent operations on the archive. For cases where this
is unwanted, you can use "copy.copy()" or call the "replace()" method
to create a modified copy in one step.

Several attributes can be set to "None" to indicate that a piece of
metadata is unused or unknown. Different "TarInfo" methods handle
"None" differently:

* The "extract()" or "extractall()" methods will ignore the
  corresponding metadata, leaving it set to a default.

* "addfile()" will fail.

* "list()" will print a placeholder string.

3.10.12 版更變: Added "replace()" and handling of "None".

class tarfile.TarInfo(name='')

   创建一个 "TarInfo" 对象。

classmethod TarInfo.frombuf(buf, encoding, errors)

   基于字符串缓冲区 *buf* 创建并返回一个 "TarInfo" 对象。

   如果缓冲区无效则会引发 "HeaderError"。

classmethod TarInfo.fromtarfile(tarfile)

   从 "TarFile" 对象 *tarfile* 读取下一个成员并将其作为 "TarInfo" 对象
   返回。

TarInfo.tobuf(format=DEFAULT_FORMAT, encoding=ENCODING, errors='surrogateescape')

   基于 "TarInfo" 对象创建一个字符串缓冲区。 有关参数的信息请参见
   "TarFile" 类的构造器。

   3.2 版更變: 使用 "'surrogateescape'" 作为 *errors* 参数的默认值。

"TarInfo" 对象具有以下公有数据属性:

TarInfo.name: str

   归档成员的名称。

TarInfo.size: int

   以字节表示的大小。

TarInfo.mtime: int | float

   Time of last modification in seconds since the epoch, as in
   "os.stat_result.st_mtime".

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.mode: int

   Permission bits, as for "os.chmod()".

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.type

   文件类型。 *type* 通常为以下常量之一: "REGTYPE", "AREGTYPE",
   "LNKTYPE", "SYMTYPE", "DIRTYPE", "FIFOTYPE", "CONTTYPE", "CHRTYPE",
   "BLKTYPE", "GNUTYPE_SPARSE"。 要更方便地确定一个 "TarInfo" 对象的类
   型，请使用下述的 "is*()" 方法。

TarInfo.linkname: str

   目标文件名的名称，该属性仅在类型为 "LNKTYPE" 和 "SYMTYPE" 的
   "TarInfo" 对象中存在。

TarInfo.uid: int

   最初保存该成员的用户的用户 ID。

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.gid: int

   最初保存该成员的用户的分组 ID。

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.uname: str

   用户名。

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.gname: str

   分组名。

   3.10.12 版更變: Can be set to "None" for "extract()" and
   "extractall()", causing extraction to skip applying this attribute.

TarInfo.pax_headers: dict

   一个包含所关联的 pax 扩展标头的键值对的字典。

TarInfo.replace(name=..., mtime=..., mode=..., linkname=...,
uid=..., gid=..., uname=..., gname=...,
deep=True)

   3.10.12 版新加入.

   Return a *new* copy of the "TarInfo" object with the given
   attributes changed. For example, to return a "TarInfo" with the
   group name set to "'staff'", use:

      new_tarinfo = old_tarinfo.replace(gname='staff')

   By default, a deep copy is made. If *deep* is false, the copy is
   shallow, i.e. "pax_headers" and any custom attributes are shared
   with the original "TarInfo" object.

"TarInfo" 对象还提供了一些便捷查询方法:

TarInfo.isfile()

   如果 "Tarinfo" 对象为普通文件则返回 "True"。

TarInfo.isreg()

   与 "isfile()" 相同。

TarInfo.isdir()

   如果为目录则返回 "True"。

TarInfo.issym()

   如果为符号链接则返回 "True"。

TarInfo.islnk()

   如果为硬链接则返回 "True"。

TarInfo.ischr()

   如果为字符设备则返回 "True"。

TarInfo.isblk()

   如果为块设备则返回 "True"。

TarInfo.isfifo()

   如果为 FIFO 则返回 "True"。.

TarInfo.isdev()

   如果为字符设备、块设备或 FIFO 之一则返回 "True"。


Extraction filters
==================

3.10.12 版新加入.

The *tar* format is designed to capture all details of a UNIX-like
filesystem, which makes it very powerful. Unfortunately, the features
make it easy to create tar files that have unintended -- and possibly
malicious -- effects when extracted. For example, extracting a tar
file can overwrite arbitrary files in various ways (e.g.  by using
absolute paths, ".." path components, or symlinks that affect later
members).

In most cases, the full functionality is not needed. Therefore,
*tarfile* supports extraction filters: a mechanism to limit
functionality, and thus mitigate some of the security issues.

也參考:

  **PEP 706**
     Contains further motivation and rationale behind the design.

The *filter* argument to "TarFile.extract()" or "extractall()" can be:

* the string "'fully_trusted'": Honor all metadata as specified in the
  archive. Should be used if the user trusts the archive completely,
  or implements their own complex verification.

* the string "'tar'": Honor most *tar*-specific features (i.e.
  features of UNIX-like filesystems), but block features that are very
  likely to be surprising or malicious. See "tar_filter()" for
  details.

* the string "'data'": Ignore or block most features specific to UNIX-
  like filesystems. Intended for extracting cross-platform data
  archives. See "data_filter()" for details.

* "None" (default): Use "TarFile.extraction_filter".

  If that is also "None" (the default), the "'fully_trusted'" filter
  will be used (for compatibility with earlier versions of Python).

  In Python 3.12, the default will emit a "DeprecationWarning".

  In Python 3.14, the "'data'" filter will become the default instead.
  It's possible to switch earlier; see "TarFile.extraction_filter".

* A callable which will be called for each extracted member with a
  TarInfo describing the member and the destination path to where the
  archive is extracted (i.e. the same path is used for all members):

     filter(/, member: TarInfo, path: str) -> TarInfo | None

  The callable is called just before each member is extracted, so it
  can take the current state of the disk into account. It can:

  * return a "TarInfo" object which will be used instead of the
    metadata in the archive, or

  * return "None", in which case the member will be skipped, or

  * raise an exception to abort the operation or skip the member,
    depending on "errorlevel". Note that when extraction is aborted,
    "extractall()" may leave the archive partially extracted. It does
    not attempt to clean up.


Default named filters
---------------------

The pre-defined, named filters are available as functions, so they can
be reused in custom filters:

tarfile.fully_trusted_filter(/, member, path)

   Return *member* unchanged.

   This implements the "'fully_trusted'" filter.

tarfile.tar_filter(/, member, path)

   Implements the "'tar'" filter.

   * Strip leading slashes ("/" and "os.sep") from filenames.

   * Refuse to extract files with absolute paths (in case the name is
     absolute even after stripping slashes, e.g. "C:/foo" on Windows).
     This raises "AbsolutePathError".

   * Refuse to extract files whose absolute path (after following
     symlinks) would end up outside the destination. This raises
     "OutsideDestinationError".

   * Clear high mode bits (setuid, setgid, sticky) and group/other
     write bits ("S_IWOTH").

   Return the modified "TarInfo" member.

tarfile.data_filter(/, member, path)

   Implements the "'data'" filter. In addition to what "tar_filter"
   does:

   * Refuse to extract links (hard or soft) that link to absolute
     paths, or ones that link outside the destination.

     This raises "AbsoluteLinkError" or "LinkOutsideDestinationError".

     Note that such files are refused even on platforms that do not
     support symbolic links.

   * Refuse to extract device files (including pipes). This raises
     "SpecialFileError".

   * For regular files, including hard links:

     * Set the owner read and write permissions ("S_IWUSR").

     * Remove the group & other executable permission ("S_IXOTH") if
       the owner doesn’t have it ("S_IXUSR").

   * For other files (directories), set "mode" to "None", so that
     extraction methods skip applying permission bits.

   * Set user and group info ("uid", "gid", "uname", "gname") to
     "None", so that extraction methods skip setting it.

   Return the modified "TarInfo" member.


Filter errors
-------------

When a filter refuses to extract a file, it will raise an appropriate
exception, a subclass of "FilterError". This will abort the extraction
if "TarFile.errorlevel" is 1 or more. With "errorlevel=0" the error
will be logged and the member will be skipped, but extraction will
continue.


Hints for further verification
------------------------------

Even with "filter='data'", *tarfile* is not suited for extracting
untrusted files without prior inspection. Among other issues, the pre-
defined filters do not prevent denial-of-service attacks. Users should
do additional checks.

Here is an incomplete list of things to consider:

* Extract to a "new temporary directory" to prevent e.g. exploiting
  pre-existing links, and to make it easier to clean up after a failed
  extraction.

* When working with untrusted data, use external (e.g. OS-level)
  limits on disk, memory and CPU usage.

* Check filenames against an allow-list of characters (to filter out
  control characters, confusables, foreign path separators, etc.).

* Check that filenames have expected extensions (discouraging files
  that execute when you “click on them”, or extension-less files like
  Windows special device names).

* Limit the number of extracted files, total size of extracted data,
  filename length (including symlink length), and size of individual
  files.

* Check for files that would be shadowed on case-insensitive
  filesystems.

Also note that:

* Tar files may contain multiple versions of the same file. Later ones
  are expected to overwrite any earlier ones. This feature is crucial
  to allow updating tape archives, but can be abused maliciously.

* *tarfile* does not protect against issues with “live” data, e.g. an
  attacker tinkering with the destination (or source) directory while
  extraction (or archiving) is in progress.


Supporting older Python versions
--------------------------------

Extraction filters were added to Python 3.12, and are backported to
older versions as security updates. To check whether the feature is
available, use e.g. "hasattr(tarfile, 'data_filter')" rather than
checking the Python version.

The following examples show how to support Python versions with and
without the feature. Note that setting "extraction_filter" will affect
any subsequent operations.

* Fully trusted archive:

     my_tarfile.extraction_filter = (lambda member, path: member)
     my_tarfile.extractall()

* Use the "'data'" filter if available, but revert to Python 3.11
  behavior ("'fully_trusted'") if this feature is not available:

     my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
                                            (lambda member, path: member))
     my_tarfile.extractall()

* Use the "'data'" filter; *fail* if it is not available:

     my_tarfile.extractall(filter=tarfile.data_filter)

  or:

     my_tarfile.extraction_filter = tarfile.data_filter
     my_tarfile.extractall()

* Use the "'data'" filter; *warn* if it is not available:

     if hasattr(tarfile, 'data_filter'):
         my_tarfile.extractall(filter='data')
     else:
         # remove this when no longer needed
         warn_the_user('Extracting may be unsafe; consider updating Python')
         my_tarfile.extractall()


Stateful extraction filter example
----------------------------------

While *tarfile*'s extraction methods take a simple *filter* callable,
custom filters may be more complex objects with an internal state. It
may be useful to write these as context managers, to be used like
this:

   with StatefulFilter() as filter_func:
       tar.extractall(path, filter=filter_func)

Such a filter can be written as, for example:

   class StatefulFilter:
       def __init__(self):
           self.file_count = 0

       def __enter__(self):
           return self

       def __call__(self, member, path):
           self.file_count += 1
           return member

       def __exit__(self, *exc_info):
           print(f'{self.file_count} files extracted')


命令行接口
==========

3.4 版新加入.

"tarfile" 模块提供了简单的命令行接口以便与 tar 归档进行交互。

如果你想要创建一个新的 tar 归档，请在 "-c" 选项后指定其名称然后列出应
当被包含的文件名:

   $ python -m tarfile -c monty.tar  spam.txt eggs.txt

传入一个字典也是可接受的:

   $ python -m tarfile -c monty.tar life-of-brian_1979/

如果你想要将一个 tar 归档提取到指定的目录，请使用 "-e" 选项:

   $ python -m tarfile -e monty.tar

你也可以通过传入目录名称将一个 tar 归档提取到不同的目录:

   $ python -m tarfile -e monty.tar  other-dir/

要获取一个 tar 归档中文件的列表，请使用 "-l" 选项:

   $ python -m tarfile -l monty.tar


命令行选项
----------

-l <tarfile>
--list <tarfile>

   列出一个 tarfile 中的文件名。

-c <tarfile> <source1> ... <sourceN>
--create <tarfile> <source1> ... <sourceN>

   基于源文件创建 tarfile。

-e <tarfile> [<output_dir>]
--extract <tarfile> [<output_dir>]

   如果未指定 *output_dir* 则会将 tarfile 提取到当前目录。

-t <tarfile>
--test <tarfile>

   检测 tarfile 是否有效。

-v, --verbose

   更详细地输出结果。

--filter <filtername>

   Specifies the *filter* for "--extract". See Extraction filters for
   details. Only string names are accepted (that is, "fully_trusted",
   "tar", and "data").

   3.10.12 版新加入.


範例
====

如何将整个 tar 归档提取到当前工作目录:

   import tarfile
   tar = tarfile.open("sample.tar.gz")
   tar.extractall()
   tar.close()

如何通过 "TarFile.extractall()" 使用生成器函数而非列表来提取一个 tar
归档的子集:

   import os
   import tarfile

   def py_files(members):
       for tarinfo in members:
           if os.path.splitext(tarinfo.name)[1] == ".py":
               yield tarinfo

   tar = tarfile.open("sample.tar.gz")
   tar.extractall(members=py_files(tar))
   tar.close()

如何基于一个文件名列表创建未压缩的 tar 归档:

   import tarfile
   tar = tarfile.open("sample.tar", "w")
   for name in ["foo", "bar", "quux"]:
       tar.add(name)
   tar.close()

使用 "with" 语句的同一个示例:

   import tarfile
   with tarfile.open("sample.tar", "w") as tar:
       for name in ["foo", "bar", "quux"]:
           tar.add(name)

如何读取一个 gzip 压缩的 tar 归档并显示一些成员信息:

   import tarfile
   tar = tarfile.open("sample.tar.gz", "r:gz")
   for tarinfo in tar:
       print(tarinfo.name, "is", tarinfo.size, "bytes in size and is ", end="")
       if tarinfo.isreg():
           print("a regular file.")
       elif tarinfo.isdir():
           print("a directory.")
       else:
           print("something else.")
   tar.close()

如何创建一个归档并使用 "TarFile.add()" 中的 *filter* 形参来重置用户信
息:

   import tarfile
   def reset(tarinfo):
       tarinfo.uid = tarinfo.gid = 0
       tarinfo.uname = tarinfo.gname = "root"
       return tarinfo
   tar = tarfile.open("sample.tar.gz", "w:gz")
   tar.add("foo", filter=reset)
   tar.close()


受支持的 tar 格式
=================

通过 "tarfile" 模块可以创建三种 tar 格式:

* The POSIX.1-1988 ustar 格式 ("USTAR_FORMAT")。 它支持最多 256 个字符
  的文件名长度和最多 100 个字符的链接名长度。 文件大小上限为 8 GiB。
  这是一种老旧但广受支持的格式。

* GNU tar 格式 ("GNU_FORMAT")。 它支持长文件名和链接名、大于 8 GiB 的
  文件以及稀疏文件。 它是 GNU/Linux 系统上的事实标准。 "tarfile" 完全
  支持针对长名称的 GNU tar 扩展，稀疏文件支持则限制为只读。

* The POSIX.1-2001 pax format ("PAX_FORMAT"). It is the most flexible
  format with virtually no limits. It supports long filenames and
  linknames, large files and stores pathnames in a portable way.
  Modern tar implementations, including GNU tar, bsdtar/libarchive and
  star, fully support extended *pax* features; some old or
  unmaintained libraries may not, but should treat *pax* archives as
  if they were in the universally supported *ustar* format. It is the
  current default format for new archives.

  它扩展了现有的 *ustar* 格式，包括用于无法以其他方式存储的附加标头。
  存在两种形式的 pax 标头：扩展标头只影响后续的文件标头，全局标头则适
  用于完整归档并会影响所有后续的文件。 为了便于移植，在 pax 标头中的所
  有数据均以 *UTF-8* 编码。

还有一些 tar 格式的其他变种，它们可以被读取但不能被创建:

* 古老的 V7 格式。 这是来自 Unix 第七版的第一个 tar 格式，它只存储常规
  文件和目录。 名称长度不能超过 100 个字符，并且没有用户/分组名信息。
  某些归档在带有非 ASCII 字符字段的情况下会产生计算错误的标头校验和。

* SunOS tar 扩展格式。 此格式是 POSIX.1-2001 pax 格式的一个变种，但并
  不保持兼容。


Unicode 问题
============

最初 tar 格式被设计用来在磁带机上生成备份，主要关注于保存文件系统信息
。 现在 tar 归档通常用于文件分发和在网络上交换归档。 最初格式（它是所
有其他格式的基础）的一个问题是它没有支持不同字符编码格式的概念。 例如
，一个在 *UTF-8* 系统上创建的普通 tar 归档如果包含非 *ASCII* 字符则将
无法在 *Latin-1* 系统上被正确读取。 文本元数据（例如文件名，链接名，用
户/分组名）将变为损坏状态。 不幸的是，没有什么办法能够自动检测一个归档
的编码格式。 pax 格式被设计用来解决这个问题。 它使用通用字符编码格式
*UTF-8* 来存储非 ASCII 元数据。

在 "tarfile" 中字符转换的细节由 "TarFile" 类的 *encoding* 和 *errors*
关键字参数控制。

*encoding* 定义了用于归档中元数据的字符编码格式。 默认值为
"sys.getfilesystemencoding()" 或是回退选项 "'ascii'"。 根据归档是被读
取还是被写入，元数据必须被解码或编码。 如果没有正确设置 *encoding*，转
换可能会失败。

*errors* 参数定义了不能被转换的字符将如何处理。 可能的取值在 错误处理
方案 小节列出。 默认方案为 "'surrogateescape'"，它也被 Python 用于文件
系统调用，参见 文件名，命令行参数，以及环境变量。。

对于 "PAX_FORMAT" 归档（默认格式），*encoding* 通常是不必要的，因为所
有元数据都使用 *UTF-8* 来存储。 *encoding* 仅在解码二进制 pax 标头或存
储带有替代字符的字符串等少数场景下会被使用。
