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'。 这是模式组合的完整列表

mode

动作

'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文件对象 的替代方法。 它应该位于位置 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)

如果 nametarfile 模块可以读取的 tar 存档文件,则返回 Truename 可以是 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

链接(在 tarfile 内部)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

以下每个常量定义了 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 版本中更改: 新归档文件的默认格式从 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, stream=False)

以下所有参数都是可选的,也可以作为实例属性访问。

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 全局头添加。

如果将 stream 设置为 True,则在读取归档文件时,不会缓存归档文件中有关文件的信息,从而节省内存。

在 3.2 版本中更改: 'surrogateescape' 用作 errors 参数的默认值。

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

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

在 3.13 版本中更改: 添加了 stream 参数。

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_ownerTrue,则使用 tar 文件中的 uid 和 gid 数字来设置提取文件的所有者/组。否则,将使用 tar 文件中的命名值。

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_ownerfilter 参数与 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 对象。

3.13 版本更改: 返回的 io.BufferedReader 对象具有 mode 属性,该属性始终等于 'rb'

TarFile.errorlevel: int

如果 errorlevel0,则在使用 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 添加到归档文件中。如果 tarinfo 表示非零大小的常规文件,则 fileobj 参数应该是一个 二进制文件,并且会从中读取 tarinfo.size 个字节并添加到归档文件中。您可以直接创建 TarInfo 对象,也可以使用 gettarinfo() 创建。

在 3.13 版本中更改: 对于非零大小的常规文件,必须提供 fileobj

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

epoch 以来最后修改的时间(以秒为单位),与 os.stat_result.st_mtime 中的时间相同。

在 3.12 版本中变更: 可以为 extract()extractall() 设置为 None,这会导致提取跳过应用此属性。

TarInfo.mode: int

权限位,与 os.chmod() 相同。

在 3.12 版本中变更: 可以为 extract()extractall() 设置为 None,这会导致提取跳过应用此属性。

TarInfo.type

文件类型。type 通常是以下常量之一:REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_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,以便提取方法跳过应用权限位。

  • 将用户和组信息(uidgidunamegname)设置为 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 指定 filter。有关详细信息,请参见 提取过滤器。仅接受字符串名称(即 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 存档通常用于文件分发和通过网络交换存档。原始格式(这是所有其他格式的基础)的一个问题是,它没有支持不同字符编码的概念。例如,如果普通 tar 存档包含非 ASCII 字符,则在 UTF-8 系统上创建的 tar 存档无法在 Latin-1 系统上正确读取。文本元数据(如文件名、链接名、用户/组名称)将显示损坏。不幸的是,没有办法自动检测存档的编码。pax 格式旨在解决此问题。它使用通用字符编码 UTF-8 存储非 ASCII 元数据。

tarfile 中的字符转换细节由 TarFile 类的 encodingerrors 关键字参数控制。

encoding 定义用于存档中元数据的字符编码。默认值为 sys.getfilesystemencoding()'ascii' 作为后备。根据存档是读取还是写入,元数据必须解码或编码。如果未正确设置 encoding,则此转换可能会失败。

errors 参数定义如何处理无法转换的字符。可能的取值列在 错误处理程序 部分中。默认方案是 'surrogateescape',Python 也将其用于其文件系统调用,请参阅 文件名,命令行参数和环境变量

对于 PAX_FORMAT 存档(默认),通常不需要 encoding,因为所有元数据都使用 UTF-8 存储。encoding 仅在极少数情况下使用,例如解码二进制 pax 标头或存储带有代理字符的字符串时。