tarfile
- 读取和写入 tar 归档文件¶
源代码: Lib/tarfile.py
tarfile
模块使得读取和写入 tar 归档文件成为可能,包括使用 gzip、bz2 和 lzma 压缩的归档文件。使用 zipfile
模块读取或写入 .zip
文件,或使用 shutil 中更高级别的函数。
一些事实和数据
支持读取/写入 POSIX.1-1988 (ustar) 格式。
支持读取/写入 GNU tar 格式,包括 longname 和 longlink 扩展,支持只读所有 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)¶
返回路径名 name 的
TarFile
对象。有关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.CompressionError¶
当不支持压缩方法或无法正确解码数据时引发。
- exception tarfile.ExtractError¶
在使用
TarFile.extract()
时,如果出现 *非致命* 错误,则会引发此异常,但前提是TarFile.errorlevel
== 2
。
- exception tarfile.HeaderError¶
如果
TarInfo.frombuf()
获取的缓冲区无效,则会引发此异常。
- exception tarfile.AbsolutePathError¶
引发以拒绝提取具有绝对路径的成员。
- exception tarfile.OutsideDestinationError¶
引发以拒绝在目标目录之外提取成员。
- exception tarfile.SpecialFileError¶
引发以拒绝提取特殊文件(例如设备或管道)。
- exception tarfile.AbsoluteLinkError¶
引发以拒绝提取具有绝对路径的符号链接。
- exception tarfile.LinkOutsideDestinationError¶
拒绝提取指向目标目录之外的符号链接。
以下常量在模块级别可用
- tarfile.ENCODING¶
默认字符编码:Windows 上为
'utf-8'
,否则为sys.getfilesystemencoding()
返回的值。
以下每个常量都定义了一种 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_FORMAT
、GNU_FORMAT
或PAX_FORMAT
之一。读取时,即使单个归档文件中存在不同的格式,也会自动检测格式。tarinfo 参数可用于将默认的
TarInfo
类替换为其他类。如果 dereference 为
False
,则将符号链接和硬链接添加到归档文件中。如果为True
,则将目标文件的内容添加到归档文件中。这对不支持符号链接的系统没有影响。如果 ignore_zeros 为
False
,则将空块视为归档文件的结尾。如果为True
,则跳过空(和无效)块,并尝试获取尽可能多的成员。这仅对读取连接或损坏的归档文件有用。debug 可以设置为从
0
(无调试消息)到3
(所有调试消息)。消息将写入sys.stderr
。errorlevel 控制如何处理提取错误,请参阅
相应的属性
。encoding 和 errors 参数定义用于读取或写入归档文件的字符编码,以及如何处理转换错误。默认设置适用于大多数用户。有关详细信息,请参阅 Unicode 问题 部分。
pax_headers 参数是一个可选的字符串字典,如果 format 为
PAX_FORMAT
,则会将其添加为 pax 全局标头。版本 3.2 中的变化: 使用
'surrogateescape'
作为 errors 参数的默认值。在 3.5 版更改: 添加了
'x'
(独占创建)模式。在 3.6 版更改: *name* 参数接受 类路径对象。
- classmethod TarFile.open(...)¶
备用构造函数。
tarfile.open()
函数实际上是此类方法的快捷方式。
- TarFile.getmember(name)¶
返回成员 name 的
TarInfo
对象。如果在归档文件中找不到 name,则会引发KeyError
。注意
如果某个成员在归档文件中出现多次,则其最后一次出现将被视为最新版本。
- TarFile.getnames()¶
以名称列表的形式返回成员。其顺序与
getmembers()
返回的列表相同。
- TarFile.list(verbose=True, *, members=None)¶
将目录表打印到
sys.stdout
。如果 verbose 为False
,则仅打印成员的名称。如果为True
,则输出类似于 ls -l 的内容。如果给出了可选的 members,则它必须是getmembers()
返回的列表的子集。版本 3.5 中的变化: 添加了 members 参数。
- 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()
方法。版本 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
(默认值),则所有 *致命* 错误都将作为OSError
或FilterError
异常引发。如果为2
,则所有 *非致命* 错误也将作为TarError
异常引发。始终会引发某些异常,例如由错误的参数类型或数据损坏引起的异常。
自定义 解压缩过滤器 应该为 *致命* 错误引发
FilterError
,为 *非致命* 错误引发ExtractError
。请注意,当引发异常时,存档可能只被部分解压缩。用户有责任清理。
- TarFile.extraction_filter¶
3.12 版新增。
用作
extract()
和extractall()
的 filter 参数默认值的 提取过滤器。该属性可以是
None
或可调用对象。与extract()
的 filter 参数不同,此属性不允许使用字符串名称。如果
extraction_filter
为None
(默认值),则在不带 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 命名,也可以指定为具有文件描述符的 文件对象 fileobj。name 可以是 类路径对象。如果给出,arcname 指定存档中文件的备用名称,否则,名称取自 fileobj 的name
属性或 name 参数。名称应为文本字符串。您可以在使用
addfile()
添加TarInfo
之前修改它的一些属性。如果文件对象不是位于文件开头的普通文件对象,则可能需要修改size
等属性。对于GzipFile
等对象就是这种情况。也可以修改name
,在这种情况下,arcname 可以是虚拟字符串。在 3.6 版更改: *name* 参数接受 类路径对象。
TarInfo 对象¶
TarInfo
对象表示 TarFile
中的一个成员。除了存储文件的所有必需属性(如文件类型、大小、时间、权限、所有者等)之外,它还提供了一些有用的方法来确定其类型。它本身*不*包含文件的数据。
TarInfo
对象由 TarFile
的方法 getmember()
、getmembers()
和 gettarinfo()
返回。
修改 getmember()
或 getmembers()
返回的对象将影响对归档的所有后续操作。对于不需要这种情况的情况,可以使用 copy.copy()
或调用 replace()
方法一步创建修改后的副本。
可以将多个属性设置为 None
以指示未使用或未知的元数据片段。不同的 TarInfo
方法以不同的方式处理 None
extract()
或extractall()
方法将忽略相应的元数据,将其设置为默认值。addfile()
将失败。list()
将打印一个占位符字符串。
- classmethod TarInfo.frombuf(buf, encoding, errors)¶
从字符串缓冲区 *buf* 创建并返回一个
TarInfo
对象。如果缓冲区无效,则引发
HeaderError
。
- TarInfo.tobuf(format=DEFAULT_FORMAT, encoding=ENCODING, errors='surrogateescape')¶
从
TarInfo
对象创建一个字符串缓冲区。有关参数的信息,请参阅TarFile
类的构造函数。版本 3.2 中的变化: 使用
'surrogateescape'
作为 errors 参数的默认值。
一个 TarInfo
对象具有以下公共数据属性
- 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 通常是以下常量之一:
REGTYPE
、AREGTYPE
、LNKTYPE
、SYMTYPE
、DIRTYPE
、FIFOTYPE
、CONTTYPE
、CHRTYPE
、BLKTYPE
、GNUTYPE_SPARSE
。要更方便地确定TarInfo
对象的类型,请使用下面的is*()
方法。
- TarInfo.linkname: str¶
目标文件名的名称,仅存在于类型为
LNKTYPE
和SYMTYPE
的TarInfo
对象中。对于符号链接 (
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.sparse¶
稀疏成员信息。
- 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
对象还提供了一些方便的查询方法
提取过滤器¶
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
。
返回修改后的
TarInfo
成员。
- tarfile.data_filter(member, path)¶
实现
'data'
过滤器。除了tar_filter
所做的之外,它还拒绝 提取链接到绝对路径或链接到目标路径之外的链接(硬链接或软链接)。
这将引发
AbsoluteLinkError
或LinkOutsideDestinationError
。请注意,即使在不支持符号链接的平台上,此类文件也会被拒绝。
拒绝 提取设备文件(包括管道)。这将引发
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
命令行选项¶
- -e <tarfile> [<output_dir>]¶
- --extract <tarfile> [<output_dir>]¶
如果未指定 *output_dir*,则将 tar 文件提取到当前目录。
- -v, --verbose¶
详细输出。
示例¶
如何将整个 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 标头或存储包含代理字符的字符串时才使用。