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'
。 这是模式组合的完整列表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)¶
如果 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()
返回的值。
以下每个常量定义了 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_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 全局头添加。如果将 stream 设置为
True
,则在读取归档文件时,不会缓存归档文件中有关文件的信息,从而节省内存。在 3.2 版本中更改: 将
'surrogateescape'
用作 errors 参数的默认值。在 3.5 版本中更改: 添加了
'x'
(独占创建)模式。在 3.6 版本中更改: name 参数接受 路径类对象。
在 3.13 版本中更改: 添加了 stream 参数。
- 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
,则使用 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_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
对象。3.13 版本更改: 返回的
io.BufferedReader
对象具有mode
属性,该属性始终等于'rb'
。
- 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 添加到归档文件中。如果 tarinfo 表示非零大小的常规文件,则 fileobj 参数应该是一个 二进制文件,并且会从中读取tarinfo.size
个字节并添加到归档文件中。您可以直接创建TarInfo
对象,也可以使用gettarinfo()
创建。在 3.13 版本中更改: 对于非零大小的常规文件,必须提供 fileobj。
- 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¶
自 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¶
目标文件名的名称,它仅存在于类型为
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 存档通常用于文件分发和通过网络交换存档。原始格式(这是所有其他格式的基础)的一个问题是,它没有支持不同字符编码的概念。例如,如果普通 tar 存档包含非 ASCII 字符,则在 UTF-8 系统上创建的 tar 存档无法在 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 标头或存储带有代理字符的字符串时。