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


11

Ubuntu 16.04を使用しています。

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

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

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

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

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


おそらく、あなたが持っている12kファイルには、名前にパターンや繰り返し文字が含まれているでしょう。tar特定のパターンで始まるすべてのファイルをすべて追加することで、それらを追加できます。これは簡単にスクリプト化できますが、必要に応じてサイズが9MB未満になることを保証しません。ただし、大きすぎるファイルをさらに分割することで、サイズを手動で調整できます。
フアンアントニオ

回答:


9

完全にパッチワークであり、迅速でラフなスケッチはそのままですが、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コマンドの入力サイズです。

これには、@ DavidFoersterによる提案された改善が含まれています。おかげでたくさん


@ dadexix86どういたしまして!
ジェイコブVlijm

シェルの呼び出しを取り除き、引数リストを直接使用しました。それでも、大きな引数リストには問題がある可能性がありtarます。標準入力ストリームでファイルリストを提供することで、呼び出しをさらに改善しようとします。
デビッドフォースター

こんにちは@DavidFoerster、私はあなたの洞察を信頼していますが、利点は何ですか?
ジェイコブVlijm

ほとんどのランタイム環境には、コマンドの引数文字列の合計長に(ソフトおよびハード)制限があり、数千のファイルを操作するときにすぐに到達します。そのためtar、適切なオプションを使用して標準入力に追加(または抽出)するファイルを指定できます。
デビッドフォースター

@DavidFoersterには問題がありますが、2番目の問題はもう実行されません。実際にはどちらもそれらの...ません
ジェイコブVlijm

6

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

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=(*.txt)txt拡張子を持つもののみを取得するように変更)を保存します$files
  • 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}第二から始まる3つの要素が返されます。ここでは、現在の値から始まるスライス取っている$iとされ$num、長い要素を。--で開始することができます任意のファイル名の場合には必要とされています-
  • ((k++)) :増分 $k

いいね!初めて、bash配列のインデックス範囲の実用的な使用を見ました。
ジョー

非常にきれいで簡潔。私にとっては、Pythonソリューションよりも理解しやすいですが、どちらもかなり良いです。それらがすべてパフォーマンスにおいてどのように比較されるのでしょうか?
DocSalvager
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.