tarfile --- 讀取與寫入 tar 封存檔案¶
原始碼:Lib/tarfile.py
tarfile 模組用於讀取與寫入 tar 封存檔案,包括使用 gzip、bz2 和 lzma 壓縮的檔案。使用 zipfile 模組來讀取或寫入 .zip 檔案,或使用 shutil 中的更高階函式。
一些事實和數據:
若相對應的模組可用,則可讀取與寫入
gzip、bz2、compression.zstd和lzma壓縮的封存檔。如果你的 CPython 副本中缺少這些選用模組 (optional modules),請查閱你的發行者(即提供 Python 給你的人)的文件。如果你就是發行者,請參閱 可選模組的需求。
支援 POSIX.1-1988 (ustar) 格式的讀取與寫入。
支援 GNU tar 格式的讀取與寫入,包括 longname 和 longlink 擴充功能,以及所有 sparse 擴充功能變體的唯讀支援,包括還原稀疏檔案 (sparse files)。
支援 POSIX.1-2001 (pax) 格式的讀取與寫入。
處理目錄、一般檔案、硬連結 (hardlinks)、符號連結 (symbolic links)、fifos、字元裝置 (character devices) 和區塊裝置 (block devices),並能夠取得與還原檔案資訊,如時間戳記、存取權限和擁有者。
在 3.3 版的變更: 新增對 lzma 壓縮的支援。
在 3.12 版的變更: 解壓縮封存檔時會使用篩選器 (filter),你可以用它來限制一些意外或危險的功能,或是明確表示這些功能是你預期的,而且封存檔是完全可信任的。
在 3.14 版的變更: 將預設解壓縮篩選器設為 data,這會禁止一些危險的功能,例如指向絕對路徑或目的地之外路徑的連結。在此之前,篩選器策略等同於 fully_trusted。
在 3.14 版的變更: 新增對 Zstandard 壓縮的支援(使用 compression.zstd)。
- 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 壓縮的檔案。
'r:zst'開啟以讀取 Zstandard 壓縮的檔案。
'x'或'x:'建立一個不壓縮的 tarfile(獨佔模式)。如果檔案已存在,則引發
FileExistsError例外。'x:gz'建立一個使用 gzip 壓縮的 tar 檔案。如果檔案已存在則引發
FileExistsError例外。'x:bz2'建立一個使用 bzip2 壓縮的 tar 檔案。如果檔案已存在,則引發
FileExistsError例外。'x:xz'建立一個使用 lzma 壓縮的 tar 檔案。如果檔案已存在,則引發
FileExistsError例外。'x:zst'建立一個使用 Zstandard 壓縮的 tar 檔案。如果檔案已存在,則引發
FileExistsError例外。'a'或'a:'開啟以附加內容且不使用壓縮。如果檔案不存在則建立它。
'w'或'w:'開啟以寫入未壓縮的內容。
'w:gz'開啟以進行 gzip 壓縮寫入。
'w:bz2'開啟以進行 bzip2 壓縮寫入。
'w:xz'開啟以進行 lzma 壓縮寫入。
'w:zst'開啟以進行 Zstandard 壓縮寫入。
請注意
'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'和'w|xz',tarfile.open()接受關鍵字引數 preset 來指定檔案的壓縮等級。對於模式
'w:zst'、'x:zst'和'w|zst',tarfile.open()接受關鍵字引數 level 來指定檔案的壓縮等級。也可以傳遞關鍵字引數 options,提供由CompressionParameter描述的進階 Zstandard 壓縮參數。可以傳遞關鍵字引數 zstd_dict 以提供ZstdDict,這是一個用於改善較小資料量壓縮效果的 Zstandard 字典。對於特殊用途,mode 有第二種格式:
'filemode|[compression]'。tarfile.open()會回傳一個TarFile物件,該物件將其資料作為區塊串流處理。不會對檔案進行隨機尋找。如果提供了 fileobj,它可以是任何具有read()或write()方法(取決於 mode)且能處理位元組的物件。bufsize 指定區塊大小,預設為20 * 512位元組。此變體可與sys.stdin.buffer、socket 檔案物件 或磁帶裝置等結合使用。但是,這樣的TarFile物件受到限制,因為它不允許隨機存取,請參閱 範例。目前可能的模式:模式
操作
'r|*'開啟以讀取 tar 區塊的串流,自動偵測壓縮格式。
'r|'開啟以讀取未壓縮的 tar 區塊串流。
'r|gz'開啟以讀取 gzip 壓縮的串流。
'r|bz2'開啟以讀取 bzip2 壓縮的串流。
'r|xz'開啟以讀取 lzma 壓縮的串流。
'r|zst'開啟以讀取 Zstandard 壓縮的串流。
'w|'開啟以寫入未壓縮的串流。
'w|gz'開啟以寫入 gzip 壓縮的串流。
'w|bz2'開啟以寫入 bzip2 壓縮的串流。
'w|xz'開啟以寫入 lzma 壓縮的串流。
'w|zst'開啟以寫入 Zstandard 壓縮的串流。
在 3.5 版的變更: 新增了
'x'(獨佔建立)模式。在 3.6 版的變更: name 參數接受類路徑物件。
在 3.12 版的變更: compresslevel 關鍵字引數也適用於串流。
在 3.14 版的變更: preset 關鍵字引數也適用於串流。
- 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¶
當拒絕解壓縮指向目的地目錄之外的符號連結時引發。
- exception tarfile.LinkFallbackError¶
當透過解壓縮另一個封存檔成員來模擬連結(硬連結或符號連結)時,如果該成員會被篩選器位置拒絕,則引發此例外。用於拒絕替代成員的例外可作為
BaseException.__context__取得。在 3.14 版被加入.
以下常數可在模組層級使用:
- 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標準模組的文件。- Archiving operations
標準
shutil模組所提供的更高階封存功能的文件。- GNU tar 手冊,基本 Tar 格式
tar 封存檔案的文件,包括 GNU tar 擴充功能。
TarFile 物件¶
TarFile 物件提供一個 tar 封存檔的介面。tar 封存檔是一個區塊序列。一個封存檔成員(一個儲存的檔案)由一個標頭區塊和後續的資料區塊組成。可以在 tar 封存檔中多次儲存一個檔案。每個封存檔成員由一個 TarInfo 物件表示,詳細資訊請參閱 TarInfo 物件。
TarFile 物件可以在 with 陳述式中用作情境管理器。當區塊完成時,它會自動關閉。請注意,如果發生例外,以寫入方式開啟的封存檔將不會被最終化 (finalized);只有內部使用的檔案物件會被關閉。使用案例請參閱 範例 一節。
在 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 功能時或作為filter='data'以支援預設安全性較低的 Python 版本(3.13 及更低版本)時明確設定此項。警告
切勿在未事先檢查的情況下解壓縮來自不受信任來源的封存檔。
從 Python 3.14 開始,預設值(
data)將防止最危險的安全問題。但是,它不會防止所有意外或不安全的行為。詳細資訊請閱讀 解壓縮篩選器 一節。在 3.5 版的變更: 新增 numeric_owner 參數。
在 3.6 版的變更: path 參數接受類路徑物件。
在 3.12 版的變更: 新增 filter 參數。
在 3.14 版的變更: filter 參數現在預設為
'data'。
- TarFile.extract(member, path='', set_attrs=True, *, numeric_owner=False, filter=None)¶
使用完整名稱將一個成員從封存檔解壓縮到目前的工作目錄。其檔案資訊會盡可能準確地解壓縮。member 可以是檔案名稱或
TarInfo物件。你可以使用 path 指定不同的目錄。path 可以是類路徑物件。除非 set_attrs 為 false,否則會設定檔案屬性 (owner, mtime, mode)。numeric_owner 和 filter 引數與
extractall()相同。備註
extract()方法不處理多個解壓縮問題。在大多數情況下,你應該考慮使用extractall()方法。警告
切勿在事先檢查之前從不受信任的來源解壓縮封存檔。詳細資訊請參閱
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(預設值),解壓縮方法將預設使用data篩選器。此屬性可以在實例上設定或在子類別中覆寫。也可以在
TarFile類別本身上設定它以設定全域預設值,儘管由於它會影響 tarfile 的所有使用,最佳做法是僅在頂層應用程式或site 組態中這樣做。要以這種方式設定全域預設值,需要將篩選器函式包裝在staticmethod()中以防止注入self引數。在 3.14 版的變更: 預設篩選器設為
data,這會禁止一些危險的功能,例如指向絕對路徑或目的地之外路徑的連結。在此之前,預設值等同於fully_trusted。
- 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 可以是一個虛設字串 (dummy string)。在 3.6 版的變更: name 參數接受類路徑物件。
TarInfo 物件¶
TarInfo 物件表示 TarFile 中的一個成員。除了儲存檔案的所有必要屬性(如檔案型別、大小、時間、權限、擁有者等)之外,它還提供一些有用的方法來確定其型別。它不包含檔案的資料本身。
TarInfo 物件由 TarFile 的方法 getmember()、getmembers() 和 gettarinfo() 回傳。
修改由 getmember() 或 getmembers() 回傳的物件將影響封存檔上的所有後續操作。對於不希望出現這種情況的情況,你可以使用 copy.copy() 或呼叫 replace() 方法來一步建立修改後的副本。
可以將多個屬性設為 None 以表示一段中介資料 (metadata) 未被使用或未知。不同的 TarInfo 方法會以不同的方式處理 None:
extract()或extractall()方法將忽略對應的中介資料,使其保持預設值。addfile()將失敗。list()將印出一個佔位字串 (placeholder string)。
- 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¶
權限位元 (permission bits),如
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-like 檔案系統的所有細節而設計的,這使它非常強大。不幸的是,這些功能使得建立在解壓縮時具有非預期甚至可能是惡意影響的 tar 檔案變得很容易。例如,解壓縮 tar 檔案可以透過各種方式覆寫任意檔案(例如使用絕對路徑、.. 路徑元件,或影響後續成員的符號連結)。
在大多數情況下,不需要完整功能。因此,tarfile 支援解壓縮篩選器:一種限制功能的機制,從而減輕某些安全性問題。
警告
沒有任何可用的篩選器會阻擋所有危險的封存檔功能。永遠不要在沒有事先檢查的情況下從不受信任的來源解壓縮封存檔。另請參閱 進一步驗證的提示。
也參考
- PEP 706
包含此設計背後的進一步動機和理由。
TarFile.extract() 或 extractall() 的 filter 引數可以是:
'fully_trusted'字串:遵守封存檔中指定的所有中介資料。應在使用者完全信任封存檔或實作自己的複雜驗證時使用。'tar'字串:遵守大多數 tar 特定功能(即 UNIX-like 檔案系統的功能),但阻擋很可能令人驚訝或惡意的功能。詳見tar_filter()。'data'字串:忽略或阻擋大多數 UNIX-like 檔案系統特定功能。用於解壓縮跨平台資料封存檔。詳見data_filter()。None(預設):使用TarFile.extraction_filter。如果該值也是
None(預設值),則會使用'data'篩選器。在 3.14 版的變更: 預設篩選器設為
data。在此之前,預設等同於fully_trusted。一個可呼叫物件,對於每個解壓縮的成員,會使用描述該成員的 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的功能外,還包括:使用
os.path.normpath()正規化連結目標(TarInfo.linkname)。請注意,這會移除內部的..元件,如果TarInfo.linkname中的路徑會遍歷符號連結,這可能會改變連結的意義。拒絕解壓縮指向絕對路徑的連結(硬連結或軟連結),或指向目的地之外的連結。
這會引發
AbsoluteLinkError或LinkOutsideDestinationError。請注意,即使在不支援符號連結的平台上,這類檔案也會被拒絕。
拒絕解壓縮裝置檔案(包括 pipe)。這會引發
SpecialFileError。對於一般檔案,包括硬連結:
對於其他檔案(目錄),將
mode設為None,讓解壓縮方法跳過套用權限位元。將使用者和群組資訊 (
uid,gid,uname,gname) 設為None,讓解壓縮方法跳過設定它們。
回傳修改後的
TarInfo成員。請注意,此篩選器並未阻擋所有危險的封存檔功能。詳細資訊請參閱 進一步驗證的提示。
在 3.14 版的變更: 連結目標現在會被正規化。
篩選器錯誤¶
當篩選器拒絕解壓縮檔案時,它會引發適當的例外,該例外是 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: # 當不再需要時移除它 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,則將 tarfile 解壓縮到目前目錄。
- -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()
如何讀取 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()
寫入範例¶
如何從檔案名稱串列建立未壓縮的 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)
如何在 TarFile.add() 的 fileobj 參數中使用 sys.stdout.buffer 建立封存檔並寫入標準輸出:
import sys
import tarfile
with tarfile.open("sample.tar.gz", "w|gz", fileobj=sys.stdout.buffer) as tar:
for name in ["foo", "bar", "quux"]:
tar.add(name)
如何在 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 標頭有兩種類型:擴充標頭(Extended headers)只影響緊接在後的檔案標頭,全域標頭(global headers)則對整個封存檔有效並影響所有後續檔案。為了可攜性,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 引數定義如何處理無法轉換的字元。可能的值列於 Error Handlers 一節。預設方案是 'surrogateescape',這也是 Python 用於檔案系統呼叫的方案,參見 File Names, Command Line Arguments, and Environment Variables。
對於 PAX_FORMAT 封存檔(預設格式),通常不需要 encoding,因為所有中介資料都使用 UTF-8 儲存。encoding 只在少數情況下使用,例如解碼二進位 pax 標頭或儲存包含代理字元的字串時。