web-dev-qa-db-ja.com

サイズ制限のある多数の類似したサイズのファイルを複数のアーカイブにtar.gzする方法

Ubuntu 16.04を使用しています。

多くのテキストファイル(ほぼ12k)を含むフォルダーがあります。 .tar.gzアップロードを受け入れて自動的に解凍するWebサイトにそれらをすべてアップロードする必要がありますが、ファイルごとに10MB(10000KB)の制限があります(したがって、特に各ファイルは独自に解凍する必要があります)。これらのファイルをすべてtar.gzした場合、結果のファイルは約72MBです。

私がやりたいのは、それぞれが10000KBより小さいサイズ/ディメンション(厳密に)の8つの.tar.gzファイルを作成することです。

あるいは、上記のすべてのファイルのサイズがほぼ同じであると想定できるため、それぞれ多かれ少なかれ同じ量の8つの.tar.gzファイルを作成したいと思います。

これら2つのタスクのいずれかを実行するにはどうすればよいですか?

GUI、CLI、またはスクリプティングを含むソリューションにはまったく問題ありません。ここでスピードを探しているのではなく、ただそれが必要です。

11
dadexix86

Totallyパッチワークとクイックラフスケッチはそのままですが、3000ファイルのディレクトリでテストされたため、以下のスクリプトは非常に高速なジョブを実行しました。

#!/usr/bin/env python3
import subprocess
import os
import sys

splitinto = 2

dr = sys.argv[1]
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)
size = n_files // splitinto

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1
for f in files:
    sub.append(f)
    if len(sub) == size:
        compress(tar, sub)
        sub = []; tar += 1

if sub:
    # taking care of left
    compress(tar, sub)

使い方

  • compress_split.pyとして空のファイルに保存します
  • Headセクションで、圧縮するファイルの数を設定します。実際には、残りのいくつかの「残り」を処理するために、常にもう1つあります。
  • ファイルを引数としてディレクトリで実行します:

    python3 /path/tocompress_split.py /directory/with/files/tocompress
    

番号付き.tar.gzファイルは、ファイルがある場所と同じディレクトリに作成されます。

説明

スクリプト:

  • ディレクトリ内のすべてのファイルをリストします
  • tarファイルにパス情報が追加されないように、ディレクトリにcdします
  • ファイルリストを読み取り、設定された区分でグループ化します
  • サブグループを番号付きファイルに圧縮します

編集

Mbのサイズでチャンクを自動的に作成します

より洗練された方法は、チャンクの最大サイズ(mb)を(2番目の)引数として使用することです。以下のスクリプトでは、チャンクがしきい値に達する(パスする)とすぐにチャンクが圧縮ファイルに書き込まれます。

スクリプトはチャンクによってトリガーされ、しきい値を超えているため、(すべての)ファイルのサイズがチャンクサイズよりも大幅に小さい場合にのみ機能します。

スクリプト:

#!/usr/bin/env python3
import subprocess
import os
import sys

dr = sys.argv[1]
chunksize = float(sys.argv[2])
os.chdir(dr)

files = os.listdir(dr)
n_files = len(files)

def compress(tar, files):
    command = ["tar", "-zcvf", "tarfile" + str(tar) + ".tar.gz", "-T", "-", "--null"]
    proc = subprocess.Popen(command, stdin=subprocess.PIPE)
    with proc:
        proc.stdin.write(b'\0'.join(map(str.encode, files)))
        proc.stdin.write(b'\0')
    if proc.returncode:
        sys.exit(proc.returncode)

sub = []; tar = 1; subsize = 0
for f in files:
    sub.append(f)
    subsize = subsize + (os.path.getsize(f)/1000000)
    if subsize >= chunksize:
        compress(tar, sub)
        sub = []; tar += 1; subsize = 0

if sub:
    # taking care of left
    compress(tar, sub)

走る:

python3 /path/tocompress_split.py /directory/with/files/tocompress chunksize

...ここで、chunksizeは、tarコマンドのinputのサイズです。

これには、@ DavidFoersterによって提案された改善が含まれています。ありがとうたくさん

9
Jacob Vlijm

純粋なシェルアプローチ:

files=(*); 
num=$((${#files[@]}/8));
k=1
for ((i=0; i<${#files[@]}; i+=$num)); do 
    tar cvzf files$k.tgz -- "${files[@]:$i:$num}"
    ((k++))
done

説明

  • files=(*):配列$filesにファイルのリスト(存在する場合はディレクトリも、files=(*.txt)に変更してtxt拡張子を持つもののみを取得)を保存します。
  • num=$((${#files[@]}/8));${#files[@]}は、配列$filesの要素数です。 $(( ))は、bashの(制限された)算術の方法です。したがって、このコマンドは$numを8で割ったファイルの数に設定します。
  • k=1:tarballに名前を付けるための単なるカウンター。
  • for ((i=0; i<${#files[@]}; i+=$num)); do:配列の値を反復処理します。 $i0(配列の最初の要素)で初期化され、$numずつ増加します。これは、すべての要素(ファイル)を確認するまで続きます。
  • tar cvzf files$i.tgz -- ${files[@]:$i:$num}:bashでは、${array[@]:start:length}を使用して配列スライス(配列の一部)を取得できるため、${array[@]:2:3}は2番目から3つの要素を返します。ここでは、$iの現在の値から始まり、$num要素の長さのスライスを取得しています。 --は、ファイル名のいずれかが-で始まる場合に必要です。
  • ((k++))$kをインクリメント
6
terdon