Ich habe dazu dieses Bash-Skript geschrieben. Es bildet im Grunde ein Array, das die Namen der Dateien enthält, die in jedes tar gehen, und startet dann tar
parallel auf allen .Es ist vielleicht nicht der effizienteste Weg, aber es wird die Arbeit so erledigen, wie Sie es möchten.Ich kann jedoch davon ausgehen, dass es große Mengen an Speicher verbraucht.
Sie müssen die Optionen am Anfang des Skripts anpassen. Möglicherweise möchten Sie auch die Tar-Optionen cvjf
ändern in der letzten Zeile (wie das Entfernen der ausführlichen Ausgabe v
für Leistung oder Änderung der Komprimierung j
bis z
, etc ...).
Skript
#!/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
Erklärung
Zunächst werden alle Dateinamen, die dem ausgewählten Muster entsprechen, im Array files
gespeichert . Als nächstes zerlegt die for-Schleife dieses Array und bildet Strings aus den Slices. Die Anzahl der Slices ist gleich der Anzahl der gewünschten Tarballs. Die resultierenden Strings werden im Array tar_files
gespeichert . Die for-Schleife fügt außerdem den Namen des resultierenden Tarballs am Anfang jeder Zeichenfolge hinzu. Die Elemente von tar_files
folgende Form annehmen (unter der Annahme von 5 Dateien/Tarball):
tar_files[0]="tar0.tar.bz2 file1 file2 file3 file4 file5"
tar_files[1]="tar1.tar.bz2 file6 file7 file8 file9 file10"
...
Die letzte Zeile des Skripts, xargs
wird verwendet, um mehrere tar
zu starten verarbeitet (bis zur maximal angegebenen Anzahl), wobei jeder ein Element von tar_files
verarbeitet Array parallel.
Test
Liste der Dateien:
$ls
a c e g i k m n p r t
b d f h j l o q s
Generierte Tarballs:$ls /tmp/tar*tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2
Hier ist ein weiteres Skript. Sie können wählen, ob Sie genau eine Million Dateien pro Segment oder genau 30 Segmente möchten. Ich habe mich in diesem Skript für Ersteres entschieden, aber für split
Schlüsselwort lässt beide Möglichkeiten zu.
#!/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
Es gibt eine Reihe von Feinheiten, die auf dieses Skript angewendet werden könnten. Die Verwendung von /tmp/chunk.
da das Präfix der Dateiliste wahrscheinlich in eine konstante Deklaration verschoben werden sollte und der Code nicht wirklich davon ausgehen sollte, dass er alles löschen kann, was /tmp/chunk.*
entspricht , aber ich habe es so belassen, eher als Machbarkeitsnachweis als als ausgefeiltes Dienstprogramm. Wenn ich dies verwenden würde, würde ich mktemp
verwenden um ein temporäres Verzeichnis für die Dateilisten zu erstellen.
Dieser tut genau das, was angefordert wurde:
#!/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
(in bash) kann auch verwendet werden, um eine Callback-Funktion auszuführen, sodass sie möglicherweise so umgeschrieben werden könnte, dass sie aussieht:
function something() {...}
find /source/path -not -type d \
| readarray -n 1000000 -t -C something asdf
GNU parallel
könnte genutzt werden, um etwas Ähnliches zu tun (ungetestet; ich habe parallel
nicht installiert, wo ich gerade bin, also beflügele ich es):
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 tar czf '/destination/path/thing_backup.{#}.tgz'
Da dies ungetestet ist, könnten Sie den --dry-run
hinzufügen arg, um zu sehen, was es tatsächlich tun wird. Das gefällt mir am besten, aber nicht jeder hat parallel
Eingerichtet. -j4
lässt es 4 Jobs gleichzeitig verwenden, -d '\0'
kombiniert mit find
ist -print0
bewirkt, dass Sonderzeichen im Dateinamen (Leerzeichen usw.) ignoriert werden. Der Rest sollte selbsterklärend sein.
Etwas Ähnliches könnte mit parallel
gemacht werden aber ich mag es nicht, weil es zufällige Dateinamen generiert:
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 --tmpdir /destination/path --files tar cz
Ich kenne [noch?] keinen Weg, um fortlaufende Dateinamen zu generieren.
xargs
könnte auch verwendet werden, aber anders als parallel
Es gibt keinen einfachen Weg, den Ausgabedateinamen zu generieren, sodass Sie am Ende etwas Dummes/Hackiges wie das Folgende tun würden:
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]"'
Das OP sagte, sie wollten Split nicht verwenden ... Ich fand das komisch als cat
wird sich ihnen wieder anschließen; dies erzeugt ein Tar und teilt es in 3-GB-Stücke auf:
tar c /source/path | split -b $((3*1024*1024*1024)) - /destination/path/thing.tar.
... und das entpackt sie in das aktuelle Verzeichnis:
cat $(\ls -1 /destination/path/thing.tar.* | sort) | tar x