Python tarfileとは?tar.gzを作成・展開する方法
tarfileは、tar形式のアーカイブを読み書きする標準ライブラリです。
gzip・bzip2・xzの圧縮に対応していて、tar.gzを1行で作れます。ZIPを扱いたい場合はPythonでZIPファイルを簡単操作!zipfileモジュール徹底解説をご覧ください。
作る
import tarfile, os
with tarfile.open("out.tar.gz", "w:gz") as tf:
tf.add("t", arcname="t")
print(os.path.getsize("out.tar.gz"), "バイト")
実行結果:
284 バイト
arcnameはアーカイブの中での名前です。指定しないと実行時のパスがそのまま入るので、/Users/you/work/tのような構造になってしまいます。
モード文字列
"w:gz"… gzip圧縮で新規作成(.tar.gz)"w:bz2"… bzip2圧縮(.tar.bz2)"w:xz"… lzma圧縮(.tar.xz)"w"… 圧縮なし"r"… 読み込み。圧縮形式は自動判別
読むときは"r"だけでよく、"r:gz"と書く必要はありません。
中身を見る
with tarfile.open("out.tar.gz") as tf:
print(tf.getnames())
for m in tf.getmembers():
kind = "ディレクトリ" if m.isdir() else "ファイル"
print(m.name, kind, m.size, oct(m.mode))
実行結果:
['t', 't/a.txt', 't/sub', 't/sub/b.txt']
t ディレクトリ 0 0o755
t/a.txt ファイル 6 0o644
t/sub ディレクトリ 0 0o755
t/sub/b.txt ファイル 6 0o644
パーミッションや所有者まで保持されるのがtarの特徴です。ZIPにはこの情報がありません。
1ファイルだけ取り出す
with tarfile.open("out.tar.gz") as tf:
f = tf.extractfile("t/a.txt")
print(repr(f.read()))
実行結果:
b'hello\n'
extractfile()はディスクに書かずにファイルオブジェクトを返します。ログを集計するだけなら展開不要です。
ディレクトリを指定するとNoneが返るので、if f is not None:で確認してください。
存在しないメンバー
with tarfile.open("out.tar.gz") as tf:
try:
tf.getmember("t/none.txt")
except KeyError as e:
print("KeyError ->", e)
実行結果:
KeyError -> "filename 't/none.txt' not found"
展開するときのセキュリティ
信頼できないtarを無条件に展開してはいけません。メンバー名に../や絶対パスが入っていると、展開先の外にファイルを書き込まれる恐れがあります。
Python 3.12以降はfilter引数が用意されました。
with tarfile.open("out.tar.gz") as tf:
tf.extractall("outdir", filter="data")
"data"… 外部から受け取ったtar向け。パーミッションや特殊ファイルを落とす"tar"… 所有者やパーミッションも復元する"fully_trusted"… 従来どおり何も検査しない
Python 3.11以前にはfilterがありません。手元の3.9で試すと次のようになります。
with tarfile.open("out.tar.gz") as tf:
try:
tf.extractall("outdir", filter="data")
except TypeError as e:
print("TypeError ->", e)
実行結果:
TypeError -> extractall() got an unexpected keyword argument 'filter'
古い環境で動かすなら、メンバー名を自分で検査してから展開します。
import os, tarfile
def safe_extract(tar_path, dest):
dest = os.path.abspath(dest)
with tarfile.open(tar_path) as tf:
for m in tf.getmembers():
target = os.path.abspath(os.path.join(dest, m.name))
if not target.startswith(dest + os.sep):
raise ValueError(f"展開先の外を指しています: {m.name}")
if m.issym() or m.islnk():
raise ValueError(f"リンクが含まれています: {m.name}")
tf.extractall(dest)
tarファイルかどうか調べる
print(tarfile.is_tarfile("out.tar.gz"))
print(tarfile.is_tarfile("notatar.txt"))
実行結果:
True
False
まとめ
- 作成は
"w:gz"、読み込みは"r"で自動判別 arcnameを付けないと実行時のパスが入るextractfile()ならディスクに書かずに読める- 外部のtarは filter=’data’ で展開する(Python 3.12以降)
関連記事
あわせて読むと理解が深まります。




