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つの要素を並列に処理します。tar
tar_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-run
argを追加して実際に行われていることを確認できます。私はこれが一番好きですが、誰もが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 %