tarfile — tar 아카이브 파일 읽고 쓰기

tarfile — tar 아카이브 파일 읽고 쓰기

tarfile 모듈은 gzip, bz2, lzma 압축을 포함한 tar 아카이브를 읽고 쓸 수 있게 해줘요. .zip 파일은 zipfile 모듈을, 고수준 아카이빙 함수는 shutil을 쓰면 돼요.

몇 가지 특징:

  • 해당 모듈이 있으면 gzip, bz2, compression.zstd, lzma로 압축된 아카이브를 읽고 써요.
  • POSIX.1-1988(ustar) 형식 읽기/쓰기 지원.
  • GNU tar 형식(longname/longlink 확장 포함) 읽기/쓰기, 희소(sparse) 확장의 모든 변형은 읽기 전용 지원(희소 파일 복원 포함).
  • POSIX.1-2001(pax) 형식 읽기/쓰기.
  • 디렉터리, 일반 파일, 하드링크, 심볼릭 링크, fifo, 문자 장치, 블록 장치를 다루고 타임스탬프, 접근 권한, 소유자 같은 파일 정보를 얻고 복원할 수 있어요.

3.3에서 lzma 압축 추가. 3.12에서 아카이브를 필터로 추출하도록 변경(놀라운/위험한 기능을 제한하거나 완전히 신뢰함을 인정). 3.14에서 기본 추출 필터를 data로 설정(절대 경로 링크나 목적지 밖 경로 같은 위험한 기능 금지). 3.14에서 compression.zstd로 Zstandard 압축 지원 추가.

출처: Python 표준 라이브러리

본문

tarfile.open(name=None, mode='r', fileobj=None, bufsize=10240, **kwargs)

pathname name에 대한 TarFile 객체를 반환해요. mode'filemode[:compression]' 형태의 문자열이고 기본은 'r'이에요. 모드 조합:

mode 동작
'r' 또는 'r:*' 투명한 압축으로 읽기용으로 열기(권장)
'r:' 압축 없이 전용 읽기
'r:gz' gzip 압축으로 읽기
'r:bz2' bzip2 압축으로 읽기
'r:xz' lzma 압축으로 읽기
'r:zst' Zstandard 압축으로 읽기
'x' 또는 'x:' 압축 없이 독점 생성(이미 있으면 FileExistsError)
'x:gz' gzip 압축으로 생성
'x:bz2' bzip2 압축으로 생성
'x:xz' lzma 압축으로 생성
'x:zst' Zstandard 압축으로 생성
'a' 또는 'a:' 압축 없이 추가용으로 열기(없으면 생성)
'w' 또는 'w:' 압축 없는 쓰기
'w:gz' gzip 압축 쓰기
'w:bz2' bzip2 압축 쓰기
'w:xz' lzma 압축 쓰기
'w:zst' Zstandard 압축 쓰기

'a:gz', 'a:bz2', 'a:xz'는 불가능해요. 특정 (압축) 파일을 읽기에 부적합한 모드면 ReadError, 지원하지 않는 압축 방법이면 CompressionError가 발생해요. fileobj를 지정하면 name 대신 그 바이너리 파일 객체를 써요(위치 0에 있어야 해요).

  • 'w:gz', 'x:gz', 'w|gz', 'w:bz2', 'x:bz2', 'w|bz2' 모드에선 compresslevel(기본 9)을 받아요.
  • 'w:xz', 'x:xz', 'w|xz'에선 preset으로 압축 수준을 받아요.
  • 'w:zst', 'x:zst', 'w|zst'에선 level(및 options, zstd_dict)을 받아요.

특수한 목적을 위한 두 번째 형식 'filemode|[compression]'이 있어요. 블록 스트림으로 데이터를 처리하는 TarFile 객체를 반환하며 임의 탐색(random seeking)이 없어요. sys.stdin.buffer, 소켓 파일 객체, 테이프 장치와 함께 써요. 가능한 모드: 'r|*', 'r|', 'r|gz', 'r|bz2', 'r|xz', 'r|zst', 'w|', 'w|gz', 'w|bz2', 'w|xz', 'w|zst'.

3.5에서 'x'(독점 생성) 모드, 3.6에서 path-like name, 3.12에서 스트림 compresslevel, 3.14에서 스트림 preset 추가.

class tarfile.TarFile — tar 아카이브 읽기/쓰기 클래스. 직접 쓰지 말고 tarfile.open()을 쓰세요.

tarfile.is_tarfile(name)nametarfile 모듈이 읽을 수 있는 tar 아카이브 파일이면 True 반환. namestr, 파일, 파일류 객체. 3.9에서 파일/파일류 객체 지원.

예외

  • exception tarfile.TarError — 모든 tarfile 예외의 기본 클래스.
  • exception tarfile.ReadError — tar 아카이브를 열 때 모듈이 처리할 수 없거나 무효일 때 발생.
  • exception tarfile.CompressionError — 압축 방법이 지원되지 않거나 데이터를 제대로 디코딩할 수 없을 때 발생.
  • exception tarfile.StreamError — 스트림형 TarFile 객체에 전형적인 제약 때문에 발생.
  • exception tarfile.ExtractErrorTarFile.extract()에서 비치명적 오류일 때 발생하되 TarFile.errorlevel == 2일 때만.
  • exception tarfile.HeaderErrorTarInfo.frombuf()가 무효한 버퍼를 받을 때 발생.
  • exception tarfile.FilterError — 필터가 거부한 멤버를 위한 기본 클래스. tarinfo 속성으로 거부된 멤버의 TarInfo.
  • exception tarfile.AbsolutePathError, OutsideDestinationError, SpecialFileError, AbsoluteLinkError, LinkOutsideDestinationError — 각각 절대 경로 멤버, 목적지 밖 멤버, 특수 파일(장치/파이프), 절대 경로 심볼릭 링크, 목적지 밖 심볼릭 링크 추출을 거부할 때 발생.
  • exception tarfile.LinkFallbackError — 링크를 다른 아카이브 멤버 추출로 에뮬레이션할 때 그 멤버가 필터 위치에서 거부될 경우 발생. 거부를 일으킨 예외는 BaseException.__context__에. 3.14 추가.

모듈 상수

  • tarfile.ENCODING — 기본 문자 인코딩: Windows에선 'utf-8', 그 외엔 sys.getfilesystemencoding() 값.
  • 타입 상수: REGTYPE, AREGTYPE(일반 파일), LNKTYPE(링크), SYMTYPE(심볼릭 링크), CHRTYPE(문자 특수 장치), BLKTYPE(블록 특수 장치), DIRTYPE(디렉터리), FIFOTYPE(FIFO 특수 장치), CONTTYPE(연속 파일), GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, GNUTYPE_SPARSE.
  • 형식 상수: USTAR_FORMAT(POSIX.1-1988), GNU_FORMAT(GNU tar), PAX_FORMAT(POSIX.1-2001), DEFAULT_FORMAT(현재 PAX_FORMAT). 3.8에서 새 아카이브 기본 형식이 GNU_FORMAT에서 PAX_FORMAT으로 변경.

TarFile 객체

TarFile 객체는 tar 아카이브에 대한 인터페이스를 제공해요. 아카이브는 블록 시퀀스이고, 멤버(저장된 파일)는 헤더 블록과 데이터 블록으로 이루어져요. 각 멤버는 TarInfo 객체로 표현돼요. 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 — 아카이브 경로(path-like 가능). fileobj가 주어지면 생략 가능.
  • mode'r'(기존 아카이브 읽기), 'a'(추가), 'w'(덮어쓰며 새로), 'x'(없을 때만 새로).
  • fileobj — 데이터 읽기/쓰기에 사용. 닫히지 않아요.
  • format — 쓰기용 아카이브 형식. 읽을 땐 자동 감지.
  • dereferenceFalse면 심볼릭/하드 링크를 추가, True면 대상 파일 내용을 추가.
  • ignore_zerosFalse면 빈 블록을 아카이브 끝으로, True면 빈/무효 블록을 건너뛰며 멤버를 최대한 얻음(연결되거나 손상된 아카이브 읽기에만 유용).
  • debug0~3, 메시지는 sys.stderr로.
  • errorlevel — 추출 오류 처리 방법.
  • encoding/errors — 아카이브 문자 인코딩과 변환 오류 처리. 기본(예: errors='surrogateescape')이 대부분에 맞아요.
  • pax_headersPAX_FORMAT일 때 pax 전역 헤더로 추가되는 문자열 딕셔너리.
  • stream=True — 읽기 시 아카이브 파일 정보를 캐시하지 않아 메모리를 아껴요. 3.13 추가.

classmethod TarFile.open(...) — 대체 생성자. tarfile.open()은 이 classmethod의 단축이에요.

  • TarFile.getmember(name) — 멤버 nameTarInfo 객체 반환. 없으면 KeyError. 멤버가 여러 번 있으면 마지막 발생을 최신으로 간주.
  • TarFile.getmembers() — 아카이브 순서대로 멤버 리스트 반환.
  • TarFile.getnames() — 멤버 이름 리스트 반환(getmembers()와 같은 순서).
  • TarFile.list(verbose=True, *, members=None) — 목차를 sys.stdout으로 출력. verboseFalse면 이름만, Truels -l 비슷.
  • TarFile.next() — 읽기로 열렸을 때 다음 멤버 TarInfo 반환, 없으면 None.
  • TarFile.extractall(path='.', members=None, *, numeric_owner=False, filter=None) — 모든 멤버를 추출. 디렉터리 정보(소유자, 수정 시간, 권한)는 모든 멤버 추출 후에 설정돼요. numeric_owner=True면 uid/gid 숫자를 사용. 경고: 신뢰하지 않는 소스의 아카이브를 검사 없이 추출하지 마세요. 3.14부터 기본 필터가 'data'. 3.5 numeric_owner, 3.6 path-like path, 3.12 filter 추가, 3.14 filter 기본 'data'.
  • TarFile.extract(member, path='', set_attrs=True, *, numeric_owner=False, filter=None) — 멤버 하나를 추출. member는 파일 이름 또는 TarInfo. set_attrs가 false면 파일 속성(소유자, mtime, mode)을 설정하지 않아요. extract()는 여러 추출 문제를 처리하지 않으니 대부분 extractall()을 쓰는 게 좋아요. 3.2 set_attrs, 3.5 numeric_owner, 3.12 filter 추가.
  • TarFile.extractfile(member) — 멤버를 파일 객체로 추출. 일반 파일이나 링크면 io.BufferedReader 객체, 그 외 기존 멤버는 None. 없으면 KeyError. 3.3에서 io.BufferedReader 반환. 3.13에서 mode 속성이 항상 'rb'.
  • TarFile.errorlevel: int0이면 오류 무시(디버그 출력에만), 1(기본)이면 치명적 오류를 OSError/FilterError로 발생, 2면 비치명적 오류도 TarError로 발생.
  • TarFile.extraction_filterextract()/extractall()filter 인자 기본값. None(기본)이면 data 필터 사용. 인스턴스나 서브클래스에서 설정 가능. 3.12 추가, 3.14에서 기본 data.
  • TarFile.add(name, arcname=None, recursive=True, *, filter=None) — 파일/디렉터리 등을 아카이브에 추가. 디렉터리는 기본 재귀 추가(정렬 순서). filterTarInfo를 받아 변경/반환, None 반환 시 제외. 3.2 filter, 3.7 재귀 정렬 추가.
  • TarFile.addfile(tarinfo, fileobj=None)TarInfo를 아카이브에 추가. 0이 아닌 크기의 일반 파일이면 fileobj(바이너리 파일)에서 tarinfo.size 바이트를 읽어 추가. 3.13에서 0이 아닌 크기 일반 파일엔 fileobj 필수.
  • TarFile.gettarinfo(name=None, arcname=None, fileobj=None)os.stat() 등의 결과에서 TarInfo 생성. arcname 생략 시 fileobj.name/name 사용. 3.6 name path-like.
  • TarFile.close()TarFile 닫기. 쓰기 모드에선 마무리 0 블록 두 개를 추가.
  • TarFile.pax_headers: dict — pax 전역 헤더 키-값 딕셔너리.

TarInfo 객체

TarInfo 객체는 TarFile의 한 멤버를 나타내고, 파일의 데이터 자체는 담지 않아요. TarFilegetmember()/getmembers()/gettarinfo()가 반환해요. 반환된 객체를 수정하면 이후 모든 아카이브 작업에 영향이 가므로, copy.copy()replace()로 복사본을 만들어 쓰는 게 좋아요.

class tarfile.TarInfo(name='')TarInfo 객체 생성.

  • classmethod TarInfo.frombuf(buf, encoding, errors) — 버퍼 buf에서 TarInfo 생성. 무효면 HeaderError.
  • classmethod TarInfo.fromtarfile(tarfile) — 다음 멤버를 읽어 TarInfo로 반환.
  • TarInfo.tobuf(format=DEFAULT_FORMAT, encoding=ENCODING, errors='surrogateescape')TarInfo에서 문자열 버퍼 생성.

공개 데이터 속성:

  • name: str — 멤버 이름. size: int — 바이트 크기. mtime: int | float — 마지막 수정 시간(epoch 초, os.stat_result.st_mtime처럼). mode: int — 권한 비트(os.chmod()처럼). type — 파일 타입(주로 REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE; is*() 메서드로 판별). linkname: strLNKTYPE/SYMTYPE에서만 존재하는 대상 파일 이름(심볼릭 링크는 링크가 있는 디렉터리 기준, 하드링크는 아카이브 루트 기준). uid, gid, uname, gname — 원래 저장한 사용자/그룹 정보. chksum: int — 헤더 체크섬. devmajor, devminor — 장치 번호. offset — tar 헤더 시작. offset_data — 파일 데이터 시작. sparse — 희소 멤버 정보. pax_headers: dict — 관련 pax 확장 헤더. 3.12에서 mtime/mode/uid/gid/uname/gnameNone으로 설정하면 추출 시 해당 속성을 건너뜀.

  • TarInfo.replace(name=..., mtime=..., mode=..., linkname=..., uid=..., gid=..., uname=..., gname=..., deep=True) — 주어진 속성을 바꾼 새 복사본 반환. 기본 깊은 복사, deep=False면 얕은 복사(공유). 3.12 추가.

    new_tarinfo = old_tarinfo.replace(gname='staff')
    

질의 메서드: isfile()/isreg()(일반 파일), isdir(), issym()(심볼릭 링크), islnk()(하드 링크), ischr()(문자 장치), isblk()(블록 장치), isfifo()(FIFO), isdev()(문자/블록/FIFO 중 하나).

추출 필터

3.12 추가. tar 형식은 UNIX 계열 파일시스템의 세부사항을 모두 담도록 설계되어 강력하지만, 추출 시 의도치 않은(어쩌면 악성) 효과를 만드는 tar 파일을 만들기 쉽죠(절대 경로, .. 경로 요소, 심볼릭 링크로 임의 파일 덮어쓰기 등). extract()/extractall()filter 인자는:

  • 'fully_trusted' — 모든 메타데이터를 그대로 반영. 아카이브를 완전히 신뢰할 때.
  • 'tar' — 대부분의 tar 특유 기능을 허용하되 놀랍거나 악성일 가능성이 큰 기능 차단.
  • 'data' — UNIX 계열 기능 대부분 무시/차단. 크로스 플랫폼 데이터 아카이브 추출용.
  • None(기본) — TarFile.extraction_filter 사용. 그것도 None이면 'data' 필터.
  • 콜러블 — 각 멤버마다 호출: filter(member: TarInfo, path: str, /) -> TarInfo | None. TarInfo를 반환하면 그 메타데이터를 사용, None 반환 시 건너뜀, 예외 발생 시 errorlevel에 따라 중단/건너뜀.

경고: 어떤 필터도 모든 위험한 기능을 막지 못해요. 신뢰하지 않는 소스의 아카이브를 검사 없이 추출하지 마세요. (참고: PEP 706)

기본 이름 필터:

  • tarfile.fully_trusted_filter(member, path)member를 그대로 반환. 'fully_trusted' 구현.
  • tarfile.tar_filter(member, path)'tar' 필터. 파일 이름의 선행 슬래시 제거, 절대 경로 거부(AbsolutePathError), .. 요소 정규화(os.path.normpath()), 심볼릭 링크를 따라 절대 경로가 목적지 밖이면 거부(OutsideDestinationError), 높은 모드 비트(setuid/setgid/sticky)와 그룹/기타 쓰기 비트 제거.
  • tarfile.data_filter(member, path)'data' 필터. tar_filter에 더해 링크 대상 정규화, 절대 경로/목적지 밖 링크 거부(AbsoluteLinkError/LinkOutsideDestinationError), 장치 파일(파이프 포함) 거부(SpecialFileError), 일반 파일엔 소유자 읽기/쓰기 권한 설정, uid/gid/uname/gnameNone으로 설정.

필터 오류: 필터가 파일 추출을 거부하면 FilterError 서브클래스를 발생시켜요. errorlevel이 1 이상이면 추출 중단, 0이면 로그만 남기고 건너뛰며 계속.

추가 검증 힌트: filter='data'여도 신뢰하지 않는 파일은 검사 없이 추출하기에 부적합해요(서비스 거부 공격 방지는 못해요). 다음과 같은 확인을 고려하세요: 새 임시 디렉터리로 추출, 필요 없으면 심볼릭 링크 금지, 외부(OS 수준) 디스크/메모리/CPU 제한, 파일 이름을 허용 문자 목록과 대조, 예상 확장자 확인, 추출 파일 수/총 크기/파일 이름 길이 제한, 대소문자 구분 없는 파일시스템에서 가려지는 파일 확인. 또한 tar 파일은 같은 파일의 여러 버전을 담을 수 있고(나중 것이 앞을 덮어씀), tarfile은 진행 중 "살아 있는" 데이터 문제를 보호하지 않아요.

구형 Python 지원: hasattr(tarfile, 'data_filter')로 기능 존재를 확인하세요.

my_tarfile.extraction_filter = (lambda member, path: member)  # fully trusted
my_tarfile.extractall()
my_tarfile.extraction_filter = getattr(tarfile, 'data_filter',
                                       (lambda member, path: member))
my_tarfile.extractall()
my_tarfile.extractall(filter=tarfile.data_filter)
if hasattr(tarfile, 'data_filter'):
    my_tarfile.extractall(filter='data')
else:
    my_tarfile.extractall()

상태 유지 필터 예제:

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')
with StatefulFilter() as filter_func:
    tar.extractall(path, filter=filter_func)

명령줄 인터페이스

3.4 추가. tarfile 모듈은 간단한 CLI를 제공해요.

$ python -m tarfile -c monty.tar  spam.txt eggs.txt
$ python -m tarfile -c monty.tar life-of-brian_1979/
$ python -m tarfile -e monty.tar
$ python -m tarfile -e monty.tar  other-dir/
$ python -m tarfile -l monty.tar

옵션: -l <tarfile>/--list(파일 목록), -c <tarfile> <source1> ... <sourceN>/--create(생성), -e <tarfile> [<output_dir>]/--extract(추출), -t <tarfile>/--test(유효성 검사), -v/--verbose, --filter <filtername>(fully_trusted/tar/data).

예제

읽기:

import tarfile
tar = tarfile.open("sample.tar.gz")
tar.extractall(filter='data')
tar.close()
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()
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()

쓰기:

import tarfile
tar = tarfile.open("sample.tar", "w")
for name in ["foo", "bar", "quux"]:
    tar.add(name)
tar.close()
import tarfile
with tarfile.open("sample.tar", "w") as tar:
    for name in ["foo", "bar", "quux"]:
        tar.add(name)
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)
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 형식

  • POSIX.1-1988 ustar(USTAR_FORMAT) — 파일 이름 최대 256자, 링크 이름 100자, 최대 8 GiB. 오래되고 제한적이지만 널리 지원.
  • GNU tar(GNU_FORMAT) — 긴 파일/링크 이름, 8 GiB 초과 파일, 희소 파일. GNU/Linux의 사실상 표준. 긴 이름 확장은 완전 지원, 희소 파일은 읽기 전용.
  • POSIX.1-2001 pax(PAX_FORMAT) — 사실상 제한 없는 가장 유연한 형식. 현재 기본. 기존 ustar를 확장 헤더로 확장(확장 헤더는 뒤 파일에만, 전역 헤더는 전체에 영향). 데이터는 UTF-8로 인코딩.

읽기만 가능한 형식: 고대 V7 형식(일반 파일/디렉터리만, 이름 100자 제한, 사용자/그룹 이름 없음), SunOS tar 확장 형식(PAX 변형이지만 호환 안 됨).

유니코드 문제

원래 tar 형식은 서로 다른 문자 인코딩 개념이 없어서, UTF-8 시스템에서 만든 아카이브가 비 ASCII 문자를 담으면 Latin-1 시스템에서 제대로 읽히지 못해요. 인코딩 자동 감지 방법은 없고, pax 형식이 UTF-8로 비 ASCII 메타데이터를 저장해 해결해요. 변환 세부사항은 TarFileencoding/errors 인자가 제어해요. encoding 기본값은 sys.getfilesystemencoding()(또는 'ascii'), errors 기본값은 'surrogateescape'예요. PAX_FORMAT에선 메타데이터가 UTF-8이라 일반적으로 encoding이 필요 없어요.

더 알아보기

  • zipfile — 표준 zipfile 모듈 문서.
  • shutil — 고수준 아카이빙 작업(shutil.make_archive() 등).
  • GNU tar 매뉴얼의 Basic Tar Format — tar 아카이브 형식과 GNU tar 확장 문서.
  • PEP 706 — 추출 필터 설계 배경.