スクリプト

スクリプト

3000万の小さなファイルを含む大きなフォルダがあります。フォルダを30個のアーカイブにバックアップしたいです。各 tar.gz ファイルには 1M ファイルがあります。複数のアーカイブに分割する理由は、大容量アーカイブを解凍するのに1ヶ月かかるからです。ファイルを解凍するときにすべてのアーカイブをまとめる必要があるため、分割するパイピングtarも機能しません。

また、各ファイルを新しいディレクトリに移動しないことを好みます。なぜなら、この巨大なフォルダにはlsも非常に痛みを伴うでしょう。

答え1

これを行うには、このbashスクリプトを作成しました。デフォルトでは、各tarに入るファイル名で配列を形成しtarこれらはすべて平行です。。これは最も効率的な方法ではないかもしれませんが、必要な方法で作業を完了できます。しかし、メモリ消費が多いと予想されます。

スクリプトの先頭でオプションを調整する必要があります。cvjf最後の行のtarオプションを変更することもできます(たとえば、vパフォーマンスを向上させるために詳細な出力を削除したり、圧縮を次にj変更するzなど)。

スクリプト

#!/bin/bash

# User configuratoin
#===================
files=(*.log)           # Set the file pattern to be used, e.g. (*.txt) or (*)
num_files_per_tar=5 # Number of files per tar
num_procs=4         # Number of tar processes to start
tar_file_dir='/tmp' # Tar files dir
tar_file_name_prefix='tar' # prefix for tar file names
tar_file_name="$tar_file_dir/$tar_file_name_prefix"

# Main algorithm
#===============
num_tars=$((${#files[@]}/num_files_per_tar))  # the number of tar files to create
tar_files=()  # will hold the names of files for each tar

tar_start=0 # gets update where each tar starts
# Loop over the files adding their names to be tared
for i in `seq 0 $((num_tars-1))`
do
  tar_files[$i]="$tar_file_name$i.tar.bz2 ${files[@]:tar_start:num_files_per_tar}"
  tar_start=$((tar_start+num_files_per_tar))
done

# Start tar in parallel for each of the strings we just constructed
printf '%s\n' "${tar_files[@]}" | xargs -n$((num_files_per_tar+1)) -P$num_procs tar cjvf

説明する

まず、選択したパターンに一致するすべてのファイル名が配列に保存されますfiles。次に、forループは配列を分割し、その分割で文字列を形成します。スライス数は必要なタールボール数と同じです。結果の文字列は配列に保存されますtar_files。 forループはまた、生成されたtarballの名前を各文字列の先頭に追加します。の要素はtar_files次の形式をとります(5つのファイル/タールボールと仮定):

tar_files[0]="tar0.tar.bz2  file1 file2 file3 file4 file5"
tar_files[1]="tar1.tar.bz2  file6 file7 file8 file9 file10"
...

スクリプトの最後の行は、複数のプロセス(指定された最大数まで)を開始xargsするために使用され、各プロセスは配列の1つの要素を並列に処理します。tartar_files

テスト

ファイルリスト:

$ls

a      c      e      g      i      k      m      n      p      r      t
b      d      f      h      j      l      o      q      s

生成された圧縮パッケージ: $ls /tmp/tar* tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2

答え2

ここに別のスクリプトがあります。セグメントごとに正確に100万個のファイルが必要か、正確に30個のセグメントが必要かを選択できます。このスクリプトでは前者を選択しましたが、splitキーワードでは2つのうちの1つを選択できます。

#!/bin/bash
#
DIR="$1"        # The source of the millions of files
TARDEST="$2"    # Where the tarballs should be placed

# Create the million-file segments
rm -f /tmp/chunk.*
find "$DIR" -type f | split -l 1000000 - /tmp/chunk.

# Create corresponding tarballs
for CHUNK in $(cd /tmp && echo chunk.*)
do
    test -f "$CHUNK" || continue

    echo "Creating tarball for chunk '$CHUNK'" >&2
    tar cTf "/tmp/$CHUNK" "$TARDEST/$CHUNK.tar"
    rm -f "/tmp/$CHUNK"
done

このスクリプトに適用できる詳細がたくさんあります。ファイルリストのプレフィックスとしての使用は/tmp/chunk.おそらく定数宣言としてプッシュされるべきであり、コードは一致するものを削除できると実際に仮定してはいけませんが/tmp/chunk.*、私はこれを完全なユーティリティではなく概念証明として保持しています。これを使用すると、mktempファイルのリストを保持する一時ディレクトリを作成します。

答え3

これがまさに必要なものです:

#!/bin/bash
ctr=0;
# Read 1M lines, strip newline chars, put the results into an array named "asdf"
while readarray -n 1000000 -t asdf; do
  ctr=$((${ctr}+1));
# "${asdf[@]}" expands each entry in the array such that any special characters in
# the filename won't cause problems
  tar czf /destination/path/asdf.${ctr}.tgz "${asdf[@]}";
# If you don't want compression, use this instead:
  #tar cf /destination/path/asdf.${ctr}.tar "${asdf[@]}";
# this is the canonical way to generate output
# for consumption by read/readarray in bash
done <(find /source/path -not -type d);

readarray(bashで)コールバック関数の実行にも使用できるので、次のように書き直すことができます。

function something() {...}
find /source/path -not -type d \
  | readarray -n 1000000 -t -C something asdf

GNUをparallel使用して同様のことを行うことができます(テストされていません。現在どこにもインストールされていないので、単にparallel使用しています)。

find /source/path -not -type d -print0 \
  | parallel -j4 -d '\0' -N1000000 tar czf '/destination/path/thing_backup.{#}.tgz'

テストされていないので、--dry-runargを追加して実際に行われていることを確認できます。私はこれが一番好きですが、誰もがparallelこれをインストールするわけではありません。 -j4一度に 4 つの操作を使用させ、-d '\0'' と組み合わせるとファイル名の特殊文字 (スペースなど) を無視することになります。残りは自明でなければなりません。find-print0

次のことを行うことは可能ですparallelが、任意のファイル名を生成するので気にしません。

find /source/path -not -type d -print0 \
  | parallel -j4 -d '\0' -N1000000 --tmpdir /destination/path --files tar cz

連続したファイル名を生成する方法はまだわかりません。

xargsまた、うまくいきますが、parallel出力ファイル名を生成する直接的な方法がないため、最終的には次のように愚かなハッカーのようなことが起こります。

find /source/path -not -type d -print0 \
  | xargs -P 4 -0 -L 1000000 bash -euc 'tar czf $(mktemp --suffix=".tgz" /destination/path/backup_XXX) "$@"'

OPは分割を使いたくないと言いました。cat再び組み合わせれば大丈夫だろうから変に見えるようです。 tar が生成され、3 GB チャンクに分割されます。

tar c /source/path | split -b $((3*1024*1024*1024)) - /destination/path/thing.tar.

...現在のディレクトリに抽出されます。

cat $(\ls -1 /destination/path/thing.tar.* | sort) | tar x

答え4

別のスクリプトがあります:https://gist.github.com/s5unty/e636a1ca698c6817330825eba67941e7

1: /boot を複数の tar ファイルにパッケージ化

$ tar -c -v --index-file=pack.index -M -L 10M -f /dev/null -F pack.sh /boot && pack.sh END
————
-rw-r--r-- 1 8.8K Sep  1 22:30 pack~1.index     <-- file list
-rw-r--r-- 1  11M Sep  1 22:30 pack~1.tar       <-- tar file (not a multiple-part, is a whole/pure tar)
-rw-r--r-- 1  116 Sep  1 22:30 pack~2.index                                                             
-rw-r--r-- 1  11M Sep  1 22:30 pack~2.tar                                                               
-rw-r--r-- 1  107 Sep  1 22:30 pack~3.index                                                             
-rw-r--r-- 1  13M Sep  1 22:30 pack~3.tar                                                               
-rw-r--r-- 1  102 Sep  1 22:30 pack~4.index                                                             
-rw-r--r-- 1  15M Sep  1 22:30 pack~4.tar       <-- big tar file,
-rw-r--r-- 1 5.3M Sep  1 22:30 pack~4.tar.part2 <-- with second part
-rw-r--r-- 1    0 Sep  1 22:30 pack~5.index                                                             
-rw-r--r-- 1  10K Sep  1 22:30 pack~5.tar                                                               
-rw-r--r-- 1    0 Sep  1 22:30 pack~x.index     <-- the last (~x)
-rw-r--r-- 1  10K Sep  1 22:30 pack~x.tar       <-- the last (~x)
————

2: 単一のtarファイルを解凍する。

$ tar -x -v -f pack~1.tar
OR
$ tar -x -v -f pack~x.tar

3: 単一のtarファイルを解凍する(複数の部分を含む、BIG tarとも呼ばれる)

$ tar -x -v -f pack~4.tar -F "pack.sh BIG" 

4: すべてのtarファイルを解凍する

$ ls -1 *.tar | xargs -I% tar -F "pack.sh BIG" -xf %

関連情報