본문 바로가기

Work

Python - shutil.make_archive()

나의 단순 반복 작업을 줄이기 위해 Python으로 잡일을 시키기로 했다.

디렉토리 트리 구조 내에 특정 이름의 디렉토리를 찾아 그 안의 파일들을 압축하는 일인데, 어리석게도 입력 인자를 범용화 하고 예외처리까지 하고 그러는 바람에 생각보다 오래 걸렸다. (그래서 아직 원하는 기능을 다 못 만들었다; 변명)

처음엔 간단하게 os.popen() 함수로 7z.exe를 호출해서 그럭저럭 원하는 결과를 얻었는데, 7zip이 -tzip 옵션을 줘도 zip 확장자를 안 달아주는 경우가 가끔 발생했다. 확률적인 건 아니고, PyCharm에서 수행하면 확장자 정상 생성, cmd 콘솔에서 수행하면 확장자 없이 생성, 이런 식이었다. 대충 구글링 해봐선 원인을 찾을 수 없었고, 이게 cmd 프로세스에서 python 프로세스 콜 한 뒤 다시 os.popen()으로 cmd 호출을 해서 그런건가 짐작만 해 본다. (그래, 결론은 '잘 모르겠고 더 이상 찾아보지 않겠다'는 얘기다)

급한대로 7zip 호출 시 output 경로에 파일이름을 'zip' 확장자까지 포함시키면 문제 없어 보인다. 이를테면,

target_path = C:\source\subpath\*.*
output_path = C:\result\subpath.zip
print(os.popen(f'7z.exe a "{output_path}" "{target_path}" -tzip -r').read())

이런 식이다.

근데 7zip이 가볍도 잘 동작하는 툴이긴 하지만 외부 의존을 한다는 점이 마음에 걸린다. 그리고 환경변수에 7z.exe의 경로를 미리 등록해 둬야 하는 점도 괜히 신경 쓰인다. 물론 나 혼자 쓰는 코드이고, 앞으로도 그럴 것이지만 온전한 프로그램이 아닌 것 같다.

당연하게도, Python엔 기본 라이브러리에 zip 생성 도구가 있다. 설치 패키지까지 치면 더 많고 다양하며 성능도 제각각이겠지만, 가장 기본적인 수단으로 7zip을 대체해 보기로 한다. 사용할 패키지는 shutil이다.

shutil.make_archive()를 쓰면 될 것 같은데, argument에 대한 설명이 쉬 이해되지 않는다. 예제 코드나 stackoverflow의 글을 읽어봐도 뭘 말하는 건지 눈에 잘 안 들어온다. (아마 내 학습 기능이 점점 퇴화해서일 수도 있다)

결국 시행착오로 찾아낸 결론은 다음과 같다.

shutil.make_archive(압축 파일 이름, 압축 파일 확장자, 압축 경로의 root, root 아래 압축 대상 경로)

함수 signature에 따르면 위 argument들은 다음과 같은 keyword로 매핑된다.

base_name: 압축 파일 이름
format: 압축 파일 확장자
root_dir: 압축 경로의 root
base_dir: root 아래 압축 대상 경로

root_dir과 base_dir의 설명이 가장 헷갈리는데, 예시로 대신 설명한다.

예)
디렉토리 구조
C:\
    ---- <dir>source
        ---- <dir>subpath
            ---- dummy_file.txt
            ---- <dir>normal
            ---- <dir>event
                ---- test_file.txt

target_path1 = C:\source\subpath
target_path2 = C:\source\subpath\event
output_path = C:\result
output_file = my_zip_file.zip
os.chdir(output_path)
shutil.make_archive('my_zip_file', 'zip', target_path1)      ### (1)

위에서 output_path를 지정하기 위해 chdir()을 통해 이동해야 한다는 점이 특이하다.
그리고 make_archive()의 base_dir은 생략할 수 있다. 설명에 의하면, 이 경우 root_dir=base_dir이 된다. 단 주의할 것이, 
base_dir은 root_dir을 기준으로 기재해야 한다는 점이다. 즉, 아래와 같이 입력해야 root_dir=base_dir이 된다. (그리고 위 코드 (1)과 동일한 결과가 된다)

shutil.make_archive('my_zip_file', 'zip', target_path1, '.\\')      ### (2)

(2)의 결과:
my_zip_file.zip
    ---- dummy_file.txt
    ---- normal
    ---- event
        ---- test_file.txt

 

근데 base_dir을 따로 지정해야 하는 경우가 왜 필요할까? 위의 target_path2의 경우를 보자.

우리가 압축하려는 트리 구조는 subpath\event\*.*를 그대로 유지하고 싶지만, subpath 디렉토리 안의 다른 파일 및 디렉토리는 제외하고 싶을 때, base_dir을 별도 지정하면 된다. 풀어쓰면,

shutil.make_archive('my_zip_file', 'zip', C:\\source\\subpath, '.\\event')      ### (3)

(3)의 결과:
my_zip_file.zip
    --- event
        ---- test_file.txt

위처럼, 압축 기준점은 root_dir로 지정하고, 실제 압축에 포함할 대상은 base_dir로 한정할 수 있다.

 

'Work' 카테고리의 다른 글

배터리 산업 뉴스  (0) 2021.11.19
고객과 프로세스  (0) 2021.11.19
Python - Failed to allocate bitmap 에러  (0) 2021.11.10
전고체 전지의 기술적 한계  (0) 2021.09.27
전기차 배터리는 무엇과 경쟁하는가?  (0) 2021.09.17