GNU/Linux >> Linux Esercitazione >  >> Linux

come creare archivi multi tar per una cartella enorme

Ho scritto questo script bash per farlo. Fondamentalmente forma un array contenente i nomi dei file da inserire in ogni tar, quindi avvia tar in parallelo su tutti .Potrebbe non essere il modo più efficiente, ma farà il lavoro come desideri. Tuttavia, posso aspettarmi che consumi grandi quantità di memoria.

Dovrai regolare le opzioni all'inizio dello script. Potresti anche voler cambiare le opzioni tar cvjf nell'ultima riga (come rimuovere l'output dettagliato v per le prestazioni o per modificare la compressione j a z , ecc ...).

Copione

#!/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

Spiegazione

Innanzitutto, tutti i nomi di file che corrispondono al pattern selezionato vengono memorizzati nell'array files . Successivamente, il ciclo for taglia questo array e forma stringhe dalle fette. Il numero delle fette è uguale al numero dei tarball desiderati. Le stringhe risultanti vengono memorizzate nell'array tar_files . Il ciclo for aggiunge anche il nome del tarball risultante all'inizio di ogni stringa. Gli elementi di tar_files assumere la seguente forma (assumendo 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"
...

L'ultima riga dello script, xargs è usato per avviare più tar processi (fino al numero massimo specificato) in cui ognuno elaborerà un elemento di tar_files array in parallelo.

Prova

Elenco dei file:

$ls

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

Tarball generati:$ls /tmp/tar*tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2


Ecco un'altra sceneggiatura. Puoi scegliere se desideri esattamente un milione di file per segmento o esattamente 30 segmenti. Ho scelto il primo in questo script, ma il split la parola chiave consente entrambe le scelte.

#!/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

Ci sono una serie di sottigliezze che potrebbero essere applicate a questo script. L'uso di /tmp/chunk. poiché il prefisso dell'elenco di file dovrebbe probabilmente essere inserito in una dichiarazione di costante e il codice non dovrebbe davvero presumere di poter eliminare qualsiasi cosa corrisponda a /tmp/chunk.* , ma l'ho lasciato in questo modo come prova di concetto piuttosto che come utilità raffinata. Se lo stessi usando, userei mktemp per creare una directory temporanea per contenere gli elenchi di file.


Questo fa esattamente ciò che è stato richiesto:

#!/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) può anche essere usato per eseguire una funzione di callback, quindi potrebbe essere potenzialmente riscritta per assomigliare a:

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

GNU parallel potrebbe essere sfruttato per fare qualcosa di simile (non testato; non ho parallel installato dove mi trovo quindi lo sto volando):

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

Dato che non è testato, puoi aggiungere --dry-run arg per vedere cosa farà effettivamente. Mi piace di più questo, ma non tutti hanno parallel installato. -j4 fa in modo che utilizzi 4 processi alla volta, -d '\0' combinato con find è -print0 fa ignorare i caratteri speciali nel nome del file (spazi bianchi, ecc.). Il resto dovrebbe essere autoesplicativo.

Qualcosa di simile potrebbe essere fatto con parallel ma non mi piace perché genera nomi di file casuali:

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

Non conosco [ancora?] un modo per fargli generare nomi di file sequenziali.

xargs potrebbe anche essere usato, ma diversamente da parallel non esiste un modo semplice per generare il nome del file di output, quindi finiresti per fare qualcosa di stupido/confuso come questo:

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]"'

L'OP ha detto che non volevano usare split ... ho pensato che fosse strano come cat li ricongiungerà benissimo; questo produce un tar e lo divide in pezzi da 3gb:

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

... e questo li decomprime nella directory corrente:

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

Linux
  1. Come impacchettare applicazioni Python per Linux

  2. Come creare una directory condivisa per tutti gli utenti in Linux

  3. Creare un archivio Tar di una directory, ad eccezione dei file nascosti?

  4. Come posso creare un file in ogni cartella?

  5. Come posso creare un utente solo per sftp?

Come creare backup con il comando tar in Linux

Comando Tar in Linux (crea ed estrai archivi)

Come creare file Tar Gz

Come decomprimere gli archivi tar bz2 xz gz in Linux

Come creare un file Tar gz

Come creare un controller di dominio su Linux per AD