たいていのOSでzip/unzipを使えますが、ひょんなことからPythonでやることにしました。
よくある内容+α程度なので、経緯を含めてパピコ。
経緯
とある自動化処理で、手元で subprocess 経由での zip してから、
1 2 |
zip_command = f"zip -r {zip_file} ./* {base_dir} -x './.git*' -x '{result_dir}/*'" res = subprocess.run(zip_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) |
転送先のコンテナに ecs:execute_command 経由で unzip していました。
特に問題はなさげですが、手元の環境が人によって変わるため、OSによって zip の中身が変わり、unzip ができない、ということが起きました。
zip に限らずこういうことは稀にありますし、そのままの構成での対処も可能ではあるでしょうが、本質的には Python で圧縮解凍をする方が正しいな~ってことで、そうすることにしました。
コード
ということでパラパラっとコーディング。圧縮
ディレクトリやファイルを複数指定しつつ、excludes的な機能を追加したもの。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import os import re import zipfile def zip(targets, compress_file, excludes=[]): zipobj = zipfile.ZipFile(compress_file, 'w', zipfile.ZIP_DEFLATED) backslash_dot = '\.' re_excludes = [re.compile(f"^{e.replace('.', backslash_dot).replace('*', '.*')}$") for e in excludes] for target in targets: if os.path.isfile(target): if zip_excludes(target, re_excludes): continue zipobj.write(target, target) continue for base, dirs, files in os.walk(target): if zip_excludes(base, re_excludes): continue for file in files: file_path = os.path.join(base, file) if zip_excludes(file_path, re_excludes): continue zipobj.write(file_path, file_path) def zip_excludes(name, re_excludes): for r in re_excludes: if r.match(name): return True return False |
解凍
全部解凍。target 指定がなければ current に。
1 2 3 |
def unzip(compress_file, target=None): zipobj = zipfile.ZipFile(compress_file, 'r') zipobj.extractall(target) |
実行例
適当なディレクトリとファイルを用意し、
1 2 3 4 5 6 7 8 |
$ mkdir -p /var/tmp/unziptest $ find /var/tmp/ziptest /etc/hostname /var/tmp/ziptest /var/tmp/ziptest/abc.log /var/tmp/ziptest/xyz.log /var/tmp/ziptest/testdir /var/tmp/ziptest/testdir/test.log /etc/hostname |
こんな感じで実行すると、
1 2 3 4 5 6 7 8 |
targets = ["/var/tmp/ziptest", "/etc/hostname"] zip_file = "/tmp/test.zip" excludes = ["*abc*"] zip(targets, zip_file, excludes) uncompress_dir = "/var/tmp/unziptest" unzip(zip_file, uncompress_dir) |
ちゃんと excludes 条件が効いて圧縮され、解凍もできています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ zipinfo /tmp/test.zip Archive: /tmp/test.zip Zip file size: 409 bytes, number of entries: 3 -rw-r--r-- 2.0 unx 0 b- defN 22-Apr-13 20:47 var/tmp/ziptest/xyz.log -rw-r--r-- 2.0 unx 0 b- defN 22-Apr-13 20:47 var/tmp/ziptest/testdir/test.log -rw-r--r-- 2.0 unx 19 b- defN 20-Feb-27 10:20 etc/hostname 3 files, 19 bytes uncompressed, 25 bytes compressed: -31.6% $ find /var/tmp/unziptest/ /var/tmp/unziptest/ /var/tmp/unziptest/var /var/tmp/unziptest/var/tmp /var/tmp/unziptest/var/tmp/ziptest /var/tmp/unziptest/var/tmp/ziptest/xyz.log /var/tmp/unziptest/var/tmp/ziptest/testdir /var/tmp/unziptest/var/tmp/ziptest/testdir/test.log /var/tmp/unziptest/etc /var/tmp/unziptest/etc/hostname |
解凍先にPythonがあるなら、ワンライナーで解凍しちゃうのもあり。
1 |
python3 -c "import zipfile; zipobj = zipfile.ZipFile('/tmp/test.zip', 'r'); zipobj.extractall()" |
最初は tarfile とかも考えたけど、どっちみち excludes な機能が完全マッチとかでイマイチなので、自前で作るしかなかったのだ。
何が言いたいかっつーと、既成コマンドは偉大っつーことですたい:-)