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以降)

関連記事

あわせて読むと理解が深まります。

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です