それを行うためにこのbashスクリプトを書きました。基本的に、各tarに入るファイルの名前を含む配列を形成し、tar
を開始します それらすべてで並行して .これは最も効率的な方法ではないかもしれませんが、思い通りに作業を完了できます.ただし、大量のメモリを消費することが予想されます.
スクリプトの開始時にオプションを調整する必要があります。また、tar オプション cvjf
を変更することもできます。 最後の行で (詳細な出力 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 ループがこの配列をスライスし、スライスから文字列を形成します。スライスの数は、必要な tarball の数と同じです。結果の文字列は配列 tar_files
に格納されます . for ループは、結果の tarball の名前も各文字列の先頭に追加します。 tar_files
の要素 次の形式を取ります (5 ファイル/tarball を想定):
tar_files[0]="tar0.tar.bz2 file1 file2 file3 file4 file5"
tar_files[1]="tar1.tar.bz2 file6 file7 file8 file9 file10"
...
スクリプトの最後の行 xargs
複数の tar
を開始するために使用されます それぞれが tar_files
の 1 つの要素を処理するプロセス (指定された最大数まで) 並列に配列します。
テスト
ファイルのリスト:
$ls
a c e g i k m n p r t
b d f h j l o q s
生成された tarball:$ls /tmp/tar*tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2
ここに別のスクリプトがあります。セグメントごとに正確に 100 万個のファイルが必要か、正確に 30 個のセグメントが必要かを選択できます。このスクリプトでは前者を使用しましたが、split
は キーワードでどちらかを選択できます。
#!/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
を使用します ファイル リストを保持するための一時ディレクトリを作成します。
これは要求されたことを正確に実行します:
#!/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) "[email protected]"'
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