Saya menulis skrip bash ini untuk melakukannya. Ini pada dasarnya membentuk larik yang berisi nama file untuk masuk ke setiap tar, lalu memulai tar
secara paralel dengan semuanya .Ini mungkin bukan cara yang paling efisien, tetapi ini akan menyelesaikan pekerjaan seperti yang Anda inginkan.Namun, saya dapat mengharapkannya menghabiskan banyak memori.
Anda perlu menyesuaikan opsi di awal skrip. Anda mungkin juga ingin mengubah opsi tar cvjf
di baris terakhir (seperti menghapus keluaran verbose v
untuk kinerja atau mengubah kompresi j
ke z
, dll ...).
Skrip
#!/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
Penjelasan
Pertama, semua nama file yang cocok dengan pola yang dipilih disimpan dalam larik files
. Selanjutnya, for loop mengiris array ini dan membentuk string dari irisan tersebut. Jumlah irisan sama dengan jumlah tarbal yang diinginkan. String yang dihasilkan disimpan dalam larik tar_files
. Perulangan for juga menambahkan nama tarball yang dihasilkan ke awal setiap string. Elemen tar_files
ambil formulir berikut (dengan asumsi 5 file/tarball):
tar_files[0]="tar0.tar.bz2 file1 file2 file3 file4 file5"
tar_files[1]="tar1.tar.bz2 file6 file7 file8 file9 file10"
...
Baris terakhir skrip, xargs
digunakan untuk memulai beberapa tar
memproses (hingga jumlah maksimum yang ditentukan) di mana masing-masing akan memproses satu elemen tar_files
array secara paralel.
Uji
Daftar file:
$ls
a c e g i k m n p r t
b d f h j l o q s
Tarball yang Dihasilkan:$ls /tmp/tar*tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2
Ini skrip lain. Anda dapat memilih apakah Anda menginginkan tepat satu juta file per segmen, atau tepatnya 30 segmen. Saya menggunakan yang pertama dalam skrip ini, tetapi split
kata kunci memungkinkan salah satu pilihan.
#!/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
Ada sejumlah kebaikan yang dapat diterapkan pada skrip ini. Penggunaan /tmp/chunk.
karena awalan daftar file mungkin harus didorong keluar ke deklarasi konstan, dan kode tidak boleh benar-benar menganggap itu dapat menghapus apa pun yang cocok dengan /tmp/chunk.*
, tapi saya membiarkannya seperti ini sebagai bukti konsep daripada utilitas yang dipoles. Jika saya menggunakan ini, saya akan menggunakan mktemp
untuk membuat direktori sementara untuk menyimpan daftar file.
Yang ini melakukan persis seperti yang diminta:
#!/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
(dalam bash) juga dapat digunakan untuk menjalankan fungsi panggilan balik, sehingga berpotensi ditulis ulang agar menyerupai:
function something() {...}
find /source/path -not -type d \
| readarray -n 1000000 -t -C something asdf
GNU parallel
dapat dimanfaatkan untuk melakukan hal serupa (belum diuji; saya tidak punya parallel
diinstal di tempat saya berada jadi saya akan melakukannya):
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 tar czf '/destination/path/thing_backup.{#}.tgz'
Karena itu belum teruji, Anda dapat menambahkan --dry-run
arg untuk melihat apa yang sebenarnya akan dilakukannya. Saya paling suka yang ini, tetapi tidak semua orang memiliki parallel
diinstal. -j4
membuatnya menggunakan 4 pekerjaan sekaligus, -d '\0'
dikombinasikan dengan find
-print0
membuatnya mengabaikan karakter khusus dalam nama file (spasi putih, dll). Sisanya harus cukup jelas.
Hal serupa dapat dilakukan dengan parallel
tapi saya tidak suka karena menghasilkan nama file acak:
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 --tmpdir /destination/path --files tar cz
Saya [belum?] tahu cara membuatnya menghasilkan nama file berurutan.
xargs
juga dapat digunakan, tetapi tidak seperti parallel
tidak ada cara langsung untuk menghasilkan nama file keluaran sehingga Anda akhirnya melakukan sesuatu yang bodoh/hacky seperti ini:
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 mengatakan mereka tidak ingin menggunakan split ... Saya pikir itu aneh seperti cat
akan bergabung kembali dengan mereka dengan baik; ini menghasilkan tar dan membaginya menjadi potongan 3gb:
tar c /source/path | split -b $((3*1024*1024*1024)) - /destination/path/thing.tar.
... dan ini menghapus tar mereka ke direktori saat ini:
cat $(\ls -1 /destination/path/thing.tar.* | sort) | tar x