tarfile - 读取和写入 tar 归档文件

源代码: Lib/tarfile.py


tarfile 模块使得读取和写入 tar 归档文件成为可能,包括使用 gzip、bz2 和 lzma 压缩的归档文件。使用 zipfile 模块读取或写入 .zip 文件,或使用 shutil 中更高级别的函数。

一些事实和数据

  • 如果相应的模块可用,则可以读取和写入 gzipbz2lzma 压缩的归档文件。

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

  • 支持读取/写入 GNU tar 格式,包括 longnamelonglink 扩展,支持只读所有 sparse 扩展的变体,包括恢复稀疏文件。

  • 支持读取/写入 POSIX.1-2001 (pax) 格式。

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

在版本 3.3 中更改: 添加了对 lzma 压缩的支持。

在版本 3.12 中更改: 使用 过滤器 提取归档文件,这使得限制意外/危险的功能或承认它们是预期且归档文件完全受信任成为可能。默认情况下,归档文件是完全受信任的,但此默认值已被弃用,并计划在 Python 3.14 中更改。

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

返回路径名 nameTarFile 对象。有关 TarFile 对象和允许的关键字参数的详细信息,请参阅 TarFile 对象

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

模式

操作

'r' 'r:*'

打开以读取,使用透明压缩(推荐)。

'r:'

打开以仅读取,不压缩。

'r:gz'

打开以读取,使用 gzip 压缩。

'r:bz2'

打开以读取,使用 bzip2 压缩。

'r:xz'

打开以读取,使用 lzma 压缩。

'x' or 'x:'

创建一个不压缩的 tar 文件。如果文件已存在,则引发 FileExistsError 异常。

'x:gz'

创建一个使用 gzip 压缩的 tar 文件。如果文件已存在,则引发 FileExistsError 异常。

'x:bz2'

创建一个使用 bzip2 压缩的 tar 文件。如果文件已存在,则引发 FileExistsError 异常。

'x:xz'

创建一个使用 lzma 压缩的 tar 文件。如果文件已存在,则引发 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 打开的 文件对象 的替代方法。它应该位于位置 0。

对于模式 'w:gz''x:gz''w|gz''w:bz2''x:bz2''w|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.buffer、套接字 文件对象 或磁带设备结合使用。但是,这样的 TarFile 对象的局限性在于它不允许随机访问,请参阅 示例。当前可能的模式

模式

操作

'r|*'

打开一个 tar 块 *流* 以进行透明压缩读取。

'r|'

打开一个未压缩的 tar 块 *流* 以进行读取。

'r|gz'

打开一个 gzip 压缩的 *流* 以进行读取。

'r|bz2'

打开一个 bzip2 压缩的 *流* 以进行读取。

'r|xz'

打开一个 lzma 压缩的 *流* 以进行读取。

'w|'

打开一个未压缩的 *流* 以进行写入。

'w|gz'

打开一个 gzip 压缩的 *流* 以进行写入。

'w|bz2'

打开一个 bzip2 压缩的 *流* 以进行写入。

'w|xz'

打开一个 lzma 压缩的 *流* 以进行写入。

在 3.5 版更改: 添加了 'x'(独占创建)模式。

在 3.6 版更改: *name* 参数接受 类路径对象

在 3.12 版更改: *compresslevel* 关键字参数也适用于流。

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

当打开一个 tar 存档时,如果 tarfile 模块无法处理该存档或该存档在某种程度上无效,则会引发此异常。

exception tarfile.CompressionError

当不支持压缩方法或无法正确解码数据时引发。

exception tarfile.StreamError

对于类流 TarFile 对象的典型限制,会引发此异常。

exception tarfile.ExtractError

在使用 TarFile.extract() 时,如果出现 *非致命* 错误,则会引发此异常,但前提是 TarFile.errorlevel== 2

exception tarfile.HeaderError

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

exception tarfile.FilterError

被过滤器 拒绝 的成员的基类。

tarinfo

有关过滤器拒绝提取的成员的信息,如 TarInfo

exception tarfile.AbsolutePathError

引发以拒绝提取具有绝对路径的成员。

exception tarfile.OutsideDestinationError

引发以拒绝在目标目录之外提取成员。

exception tarfile.SpecialFileError

引发以拒绝提取特殊文件(例如设备或管道)。

exception tarfile.AbsoluteLinkError

引发以拒绝提取具有绝对路径的符号链接。

exception tarfile.LinkOutsideDestinationError

拒绝提取指向目标目录之外的符号链接。

以下常量在模块级别可用

tarfile.ENCODING

默认字符编码:Windows 上为 'utf-8',否则为 sys.getfilesystemencoding() 返回的值。

tarfile.REGTYPE
tarfile.AREGTYPE

一个普通文件 type

tarfile.LNKTYPE

一个链接(在 tar 文件内) type

tarfile.SYMTYPE

一个符号链接 type

tarfile.CHRTYPE

一个字符特殊设备 type

tarfile.BLKTYPE

一个块特殊设备 type

tarfile.DIRTYPE

一个目录 type

tarfile.FIFOTYPE

一个 FIFO 特殊设备 type

tarfile.CONTTYPE

一个连续文件 type

tarfile.GNUTYPE_LONGNAME

一个 GNU tar 长文件名 type

一个 GNU tar 长链接 type

tarfile.GNUTYPE_SPARSE

一个 GNU tar 稀疏文件 type

以下每个常量都定义了一种 tar 归档格式,tarfile 模块能够创建这些格式。有关详细信息,请参阅 支持的 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 版更改: 新归档文件的默认格式已从 GNU_FORMAT 更改为 PAX_FORMAT

另请参阅

模块 zipfile

zipfile 标准模块的文档。

归档操作

标准 shutil 模块提供的更高级别归档工具的文档。

GNU tar 手册,基本 Tar 格式

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 可以是 类路径对象。如果给出了 fileobj,则可以省略它。在这种情况下,将使用文件对象的 name 属性(如果存在)。

mode 可以是 'r',表示从现有归档文件中读取数据;'a',表示将数据追加到现有文件中;'w',表示创建一个新文件并覆盖现有文件;或者 'x',表示仅当文件不存在时才创建新文件。

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

注意

TarFile 关闭时,fileobj 不会关闭。

format 控制写入归档文件的格式。它必须是模块级别定义的常量 USTAR_FORMATGNU_FORMATPAX_FORMAT 之一。读取时,即使单个归档文件中存在不同的格式,也会自动检测格式。

tarinfo 参数可用于将默认的 TarInfo 类替换为其他类。

如果 dereferenceFalse,则将符号链接和硬链接添加到归档文件中。如果为 True,则将目标文件的内容添加到归档文件中。这对不支持符号链接的系统没有影响。

如果 ignore_zerosFalse,则将空块视为归档文件的结尾。如果为 True,则跳过空(和无效)块,并尝试获取尽可能多的成员。这仅对读取连接或损坏的归档文件有用。

debug 可以设置为从 0(无调试消息)到 3(所有调试消息)。消息将写入 sys.stderr

errorlevel 控制如何处理提取错误,请参阅 相应的属性

encodingerrors 参数定义用于读取或写入归档文件的字符编码,以及如何处理转换错误。默认设置适用于大多数用户。有关详细信息,请参阅 Unicode 问题 部分。

pax_headers 参数是一个可选的字符串字典,如果 formatPAX_FORMAT,则会将其添加为 pax 全局标头。

版本 3.2 中的变化: 使用 'surrogateescape' 作为 errors 参数的默认值。

在 3.5 版更改: 添加了 'x'(独占创建)模式。

在 3.6 版更改: *name* 参数接受 类路径对象

classmethod TarFile.open(...)

备用构造函数。 tarfile.open() 函数实际上是此类方法的快捷方式。

TarFile.getmember(name)

返回成员 nameTarInfo 对象。如果在归档文件中找不到 name,则会引发 KeyError

注意

如果某个成员在归档文件中出现多次,则其最后一次出现将被视为最新版本。

TarFile.getmembers()

TarInfo 对象列表的形式返回归档文件的成员。该列表的顺序与归档文件中成员的顺序相同。

TarFile.getnames()

以名称列表的形式返回成员。其顺序与 getmembers() 返回的列表相同。

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

将目录表打印到 sys.stdout。如果 verboseFalse,则仅打印成员的名称。如果为 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 中的命名值。

*filter* 参数指定在解压缩之前如何修改或拒绝 members。有关详细信息,请参阅 解压缩过滤器。建议根据您需要支持的 *tar* 功能显式设置此参数。

警告

切勿在未经事先检查的情况下从不受信任的来源解压缩存档。可能会在 *path* 之外创建文件,例如,以 "/" 开头的绝对文件名或包含两个点 ".." 的文件名。

设置 filter='data' 以防止最危险的安全问题,并阅读 解压缩过滤器 部分以获取详细信息。

版本 3.5 中的变化: 添加了 *numeric_owner* 参数。

版本 3.6 中的变化: *path* 参数接受 类路径对象

版本 3.12 中的变化: 添加了 *filter* 参数。

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

使用其全名将存档中的成员解压缩到当前工作目录。将尽可能准确地提取其文件信息。*member* 可以是文件名或 TarInfo 对象。您可以使用 *path* 指定其他目录。*path* 可以是 类路径对象。除非 *set_attrs* 为 false,否则将设置文件属性(所有者、mtime、模式)。

*numeric_owner* 和 *filter* 参数与 extractall() 相同。

注意

extract() 方法不处理多个解压缩问题。在大多数情况下,您应该考虑使用 extractall() 方法。

警告

请参阅 extractall() 的警告。

设置 filter='data' 以防止最危险的安全问题,并阅读 解压缩过滤器 部分以获取详细信息。

版本 3.2 中的变化: 添加了 *set_attrs* 参数。

版本 3.5 中的变化: 添加了 *numeric_owner* 参数。

版本 3.6 中的变化: *path* 参数接受 类路径对象

版本 3.12 中的变化: 添加了 *filter* 参数。

TarFile.extractfile(member)

将存档中的成员作为文件对象解压缩。*member* 可以是文件名或 TarInfo 对象。如果 *member* 是常规文件或链接,则返回 io.BufferedReader 对象。对于所有其他现有成员,将返回 None。如果 *member* 未出现在存档中,则会引发 KeyError

版本 3.3 中的变化: 返回 io.BufferedReader 对象。

TarFile.errorlevel: int

如果 *errorlevel* 为 0,则在使用 TarFile.extract()TarFile.extractall() 时,将忽略错误。但是,当 *debug* 大于 0 时,它们将作为错误消息出现在调试输出中。如果为 1(默认值),则所有 *致命* 错误都将作为 OSErrorFilterError 异常引发。如果为 2,则所有 *非致命* 错误也将作为 TarError 异常引发。

始终会引发某些异常,例如由错误的参数类型或数据损坏引起的异常。

自定义 解压缩过滤器 应该为 *致命* 错误引发 FilterError,为 *非致命* 错误引发 ExtractError

请注意,当引发异常时,存档可能只被部分解压缩。用户有责任清理。

TarFile.extraction_filter

3.12 版新增。

用作 extract()extractall()filter 参数默认值的 提取过滤器

该属性可以是 None 或可调用对象。与 extract()filter 参数不同,此属性不允许使用字符串名称。

如果 extraction_filterNone(默认值),则在不带 filter 参数的情况下调用提取方法将引发 DeprecationWarning,并回退到 fully_trusted 过滤器,其危险行为与先前版本的 Python 相匹配。

在 Python 3.14+ 中,保留 extraction_filter=None 将导致提取方法默认使用 data 过滤器。

该属性可以在实例上设置或在子类中覆盖。也可以在 TarFile 类本身上设置它以设置全局默认值,但是,由于它会影响 tarfile 的所有使用,因此最佳做法是仅在顶级应用程序或 site 配置 中执行此操作。要以这种方式设置全局默认值,需要将过滤器函数包装在 staticmethod() 中,以防止注入 self 参数。

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,它应该是一个 二进制文件,并且从中读取 tarinfo.size 字节并添加到存档中。您可以直接创建 TarInfo 对象,也可以使用 gettarinfo() 创建。

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

从现有文件上 os.stat() 或等效函数的结果创建一个 TarInfo 对象。该文件可以通过 name 命名,也可以指定为具有文件描述符的 文件对象 fileobjname 可以是 类路径对象。如果给出,arcname 指定存档中文件的备用名称,否则,名称取自 fileobjname 属性或 name 参数。名称应为文本字符串。

您可以在使用 addfile() 添加 TarInfo 之前修改它的一些属性。如果文件对象不是位于文件开头的普通文件对象,则可能需要修改 size 等属性。对于 GzipFile 等对象就是这种情况。也可以修改 name,在这种情况下,arcname 可以是虚拟字符串。

在 3.6 版更改: *name* 参数接受 类路径对象

TarFile.close()

关闭 TarFile。在写入模式下,两个结尾的零块将附加到存档中。

TarFile.pax_headers: dict

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

TarInfo 对象

TarInfo 对象表示 TarFile 中的一个成员。除了存储文件的所有必需属性(如文件类型、大小、时间、权限、所有者等)之外,它还提供了一些有用的方法来确定其类型。它本身*不*包含文件的数据。

TarInfo 对象由 TarFile 的方法 getmember()getmembers()gettarinfo() 返回。

修改 getmember()getmembers() 返回的对象将影响对归档的所有后续操作。对于不需要这种情况的情况,可以使用 copy.copy() 或调用 replace() 方法一步创建修改后的副本。

可以将多个属性设置为 None 以指示未使用或未知的元数据片段。不同的 TarInfo 方法以不同的方式处理 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

纪元 以来的最后修改时间(以秒为单位),如 os.stat_result.st_mtime 中所示。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.mode: int

权限位,如 os.chmod() 中所示。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.type

文件类型。type 通常是以下常量之一:REGTYPEAREGTYPELNKTYPESYMTYPEDIRTYPEFIFOTYPECONTTYPECHRTYPEBLKTYPEGNUTYPE_SPARSE。要更方便地确定 TarInfo 对象的类型,请使用下面的 is*() 方法。

TarInfo.linkname: str

目标文件名的名称,仅存在于类型为 LNKTYPESYMTYPETarInfo 对象中。

对于符号链接 (SYMTYPE),linkname 是相对于包含该链接的目录的。对于硬链接 (LNKTYPE),linkname 是相对于存档根目录的。

TarInfo.uid: int

最初存储此成员的用户的用户 ID。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.gid: int

最初存储此成员的用户的组 ID。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.uname: str

用户名。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.gname: str

组名。

在 3.12 版更改: 可以为 extract()extractall() 设置为 None,从而导致提取跳过应用此属性。

TarInfo.chksum: int

标头校验和。

TarInfo.devmajor: int

设备主号码。

TarInfo.devminor: int

设备次号码。

TarInfo.offset: int

tar 标头从此处开始。

TarInfo.offset_data: int

文件数据从此处开始。

TarInfo.sparse

稀疏成员信息。

TarInfo.pax_headers: dict

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

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

3.12 版新增。

返回 TarInfo 对象的一个*新*副本,其中给定的属性已更改。例如,要返回一个组名设置为 'staff'TarInfo,请使用

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

默认情况下,将进行深拷贝。如果 deep 为 false,则拷贝为浅拷贝,即 pax_headers 和任何自定义属性都与原始 TarInfo 对象共享。

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

提取过滤器

3.12 版新增。

tar 格式旨在捕获类 UNIX 文件系统的所有细节,这使其非常强大。不幸的是,这些特性使得创建在提取时会产生意外(甚至可能是恶意)影响的 tar 文件变得很容易。例如,提取 tar 文件可以通过各种方式覆盖任意文件(例如,通过使用绝对路径、.. 路径组件或影响后续成员的符号链接)。

在大多数情况下,不需要完整的功能。因此,tarfile 支持提取过滤器:一种限制功能的机制,从而减轻一些安全问题。

另请参阅

PEP 706

包含设计背后的更多动机和基本原理。

TarFile.extract()extractall()filter 参数可以是

  • 字符串 'fully_trusted':按照存档中指定的尊重所有元数据。如果用户完全信任存档或实现了自己的复杂验证,则应使用此选项。

  • 字符串 'tar':尊重大多数特定于 tar 的特性(即类 UNIX 文件系统的特性),但阻止很可能令人惊讶或恶意的特性。有关详细信息,请参阅 tar_filter()

  • 字符串 'data':忽略或阻止大多数特定于类 UNIX 文件系统的特性。适用于提取跨平台数据存档。有关详细信息,请参阅 data_filter()

  • None(默认):使用 TarFile.extraction_filter

    如果该值也是 None(默认值),则引发 DeprecationWarning,并回退到 'fully_trusted' 过滤器,其危险行为与以前版本的 Python 相匹配。

    在 Python 3.14 中,'data' 过滤器将成为默认过滤器。可以提前切换;请参阅 TarFile.extraction_filter

  • 一个可调用对象,它将为每个提取的成员调用,并带有一个描述该成员的 TarInfo 和存档提取到的目标路径(即,所有成员使用相同的路径)

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

    可调用对象在提取每个成员之前调用,因此它可以考虑磁盘的当前状态。它可以

    • 返回一个 TarInfo 对象,该对象将用于代替存档中的元数据,或者

    • 返回 None,在这种情况下,该成员将被跳过,或者

    • 引发异常以中止操作或跳过成员,具体取决于 errorlevel。请注意,当提取中止时,extractall() 可能会使存档部分提取。它不会尝试清理。

默认命名过滤器

预定义的命名过滤器以函数的形式提供,因此可以在自定义过滤器中重复使用

tarfile.fully_trusted_filter(member, path)

原样返回 member

这实现了 'fully_trusted' 过滤器。

tarfile.tar_filter(member, path)

实现 'tar' 过滤器。

  • 从文件名中删除前导斜杠(/os.sep)。

  • 拒绝 提取具有绝对路径的文件(如果即使在删除斜杠后名称仍然是绝对路径,例如 Windows 上的 C:/foo)。这将引发 AbsolutePathError

  • 拒绝 提取其绝对路径(跟随符号链接后)最终位于目标路径之外的文件。这将引发 OutsideDestinationError

  • 清除高模式位(setuid、setgid、sticky)和组/其他写入位(S_IWGRP | S_IWOTH)。

返回修改后的 TarInfo 成员。

tarfile.data_filter(member, path)

实现 'data' 过滤器。除了 tar_filter 所做的之外,它还

  • 拒绝 提取链接到绝对路径或链接到目标路径之外的链接(硬链接或软链接)。

    这将引发 AbsoluteLinkErrorLinkOutsideDestinationError

    请注意,即使在不支持符号链接的平台上,此类文件也会被拒绝。

  • 拒绝 提取设备文件(包括管道)。这将引发 SpecialFileError

  • 对于常规文件,包括硬链接

  • 对于其他文件(目录),将 mode 设置为 None,以便提取方法跳过应用权限位。

  • 将用户和组信息 (uid, gid, uname, gname) 设置为 None,以便提取方法跳过设置它。

返回修改后的 TarInfo 成员。

过滤错误

当过滤器拒绝提取文件时,它将引发一个适当的异常,即 FilterError 的子类。如果 TarFile.errorlevel 为 1 或更大,这将中止提取。如果 errorlevel=0,则错误将被记录,并且该成员将被跳过,但提取将继续。

进一步验证的提示

即使使用 filter='data',*tarfile* 也不适合在没有事先检查的情况下提取不受信任的文件。除其他问题外,预定义的过滤器不能防止拒绝服务攻击。用户应该进行额外的检查。

以下是不完整的注意事项列表

  • 提取到 新的 临时 目录,以防止例如利用预先存在的链接,并使其更容易在提取失败后清理。

  • 处理不受信任的数据时,请对磁盘、内存和 CPU 使用量使用外部(例如操作系统级别)限制。

  • 根据允许的字符列表检查文件名(以过滤掉控制字符、容易混淆的字符、外部路径分隔符等)。

  • 检查文件名是否具有预期的扩展名(不鼓励使用“单击时”执行的文件,或不带扩展名的文件,如 Windows 特殊设备名称)。

  • 限制提取文件的数量、提取数据的总大小、文件名长度(包括符号链接长度)和单个文件的大小。

  • 检查在不区分大小写的文件系统上会被隐藏的文件。

另请注意

  • Tar 文件可能包含同一文件的多个版本。后面的版本应该会覆盖任何先前的版本。此功能对于更新磁带存档至关重要,但可能会被恶意滥用。

  • *tarfile* 不保护“实时”数据的问题,例如攻击者在提取(或归档)过程中篡改目标(或源)目录。

支持旧版 Python

提取过滤器已添加到 Python 3.12 中,但可能会作为安全更新向后移植到旧版本。要检查该功能是否可用,请使用例如 hasattr(tarfile, 'data_filter') 而不是检查 Python 版本。

以下示例展示了如何支持带有和不带有该功能的 Python 版本。请注意,设置 extraction_filter 将影响任何后续操作。

  • 完全受信任的存档

    my_tarfile.extraction_filter = (lambda member, path: member)
    my_tarfile.extractall()
    
  • 如果可用,请使用 'data' 过滤器,但如果此功能不可用,则恢复为 Python 3.11 行为 ('fully_trusted')

    my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
                                           (lambda member, path: member))
    my_tarfile.extractall()
    
  • 使用 'data' 过滤器;如果不可用则*失败*

    my_tarfile.extractall(filter=tarfile.data_filter)
    

    my_tarfile.extraction_filter = tarfile.data_filter
    my_tarfile.extractall()
    
  • 使用 'data' 过滤器;如果不可用则*警告*

    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()
    

有状态提取过滤器示例

虽然 *tarfile* 的提取方法采用简单的 *filter* 可调用对象,但自定义过滤器可能是具有内部状态的更复杂对象。将它们编写为上下文管理器可能很有用,如下所示

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

例如,可以将这样的过滤器编写为

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>

列出 tar 文件中的文件。

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

从源文件创建 tar 文件。

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

如果未指定 *output_dir*,则将 tar 文件提取到当前目录。

-t <tarfile>
--test <tarfile>

测试 tar 文件是否有效。

-v, --verbose

详细输出。

--filter <filtername>

指定 --extract 的 *过滤器*。有关详细信息,请参阅 提取过滤器。仅接受字符串名称(即 fully_trustedtardata)。

示例

如何将整个 tar 存档解压缩到当前工作目录

import tarfile
tar = tarfile.open("sample.tar.gz")
tar.extractall(filter='data')
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 格式

  • POSIX.1-1988 ustar 格式(USTAR_FORMAT)。它支持最长 256 个字符的文件名和最长 100 个字符的链接名。最大文件大小为 8 GiB。这是一种古老且有限但得到广泛支持的格式。

  • GNU tar 格式(GNU_FORMAT)。它支持长文件名和链接名、大于 8 GiB 的文件和稀疏文件。它是 GNU/Linux 系统上的事实标准。tarfile 完全支持长文件名的 GNU tar 扩展,稀疏文件支持是只读的。

  • POSIX.1-2001 pax 格式(PAX_FORMAT)。它是最灵活的格式,几乎没有限制。它支持长文件名和链接名、大文件,并以可移植的方式存储路径名。现代 tar 实现,包括 GNU tar、bsdtar/libarchive 和 star,完全支持扩展的 *pax* 功能;一些旧的或未维护的库可能不支持,但应将 *pax* 存档视为采用普遍支持的 *ustar* 格式。它是新存档的当前默认格式。

    它使用额外的标头扩展了现有的 *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 标头或存储包含代理字符的字符串时才使用。