Da quando hai menzionato: non sono limitato a rsync:
Script per mantenere il mirror, consentendo di aggiungere file extra alla destinazione
Di seguito uno script che fa esattamente quello che descrivi.
Lo script può essere eseguito in modalità dettagliata (da impostare nello script), che genererà l'avanzamento del backup (mirroring). Non c'è bisogno di dire che può essere utilizzato anche per registrare i backup:
Opzione dettagliata
Il concetto
1. Al primo backup, lo script:
- crea un file (nella directory di destinazione), in cui sono elencati tutti i file e le directory;
.recentfiles
- crea una copia esatta (mirror) di tutti i file e le directory nella directory di destinazione
2. Al successivo e così via backup
- Lo script confronta la struttura delle directory e le date di modifica dei file. I nuovi file e directory nell'origine vengono copiati nel mirror. Allo stesso tempo viene creato un secondo file (temporaneo), che elenca i file e le directory correnti nella directory di origine;
.currentfiles
.
- Successivamente,
.recentfiles
(elencando la situazione sul backup precedente) viene confrontato .currentfiles
. Solo i file da .recentfiles
cui non sono presenti .currentfiles
vengono ovviamente rimossi dall'origine e verranno rimossi dalla destinazione.
- I file aggiunti manualmente alla cartella di destinazione non vengono comunque "visti" dallo script e vengono lasciati soli.
- Infine, il temporaneo
.currentfiles
viene rinominato per .recentfiles
servire il ciclo di backup successivo e così via.
Il copione
#!/usr/bin/env python3
import os
import sys
import shutil
dr1 = sys.argv[1]; dr2 = sys.argv[2]
# --- choose verbose (or not)
verbose = True
# ---
recentfiles = os.path.join(dr2, ".recentfiles")
currentfiles = os.path.join(dr2, ".currentfiles")
if verbose:
print("Counting items in source...")
file_count = sum([len(files)+len(d) for r, d, files in os.walk(dr1)])
print(file_count, "items in source")
print("Reading directory & file structure...")
done = 0; chunk = int(file_count/5); full = chunk*5
def show_percentage(done):
if done % chunk == 0:
print(str(int(done/full*100))+"%...", end = " ")
for root, dirs, files in os.walk(dr1):
for dr in dirs:
if verbose:
if done == 0:
print("Updating mirror...")
done = done + 1
show_percentage(done)
target = os.path.join(root, dr).replace(dr1, dr2)
source = os.path.join(root, dr)
open(currentfiles, "a+").write(target+"\n")
if not os.path.exists(target):
shutil.copytree(source, target)
for f in files:
if verbose:
done = done + 1
show_percentage(done)
target = os.path.join(root, f).replace(dr1, dr2)
source = os.path.join(root, f)
open(currentfiles, "a+").write(target+"\n")
sourcedit = os.path.getmtime(source)
try:
if os.path.getmtime(source) > os.path.getmtime(target):
shutil.copy(source, target)
except FileNotFoundError:
shutil.copy(source, target)
if verbose:
print("\nChecking for deleted files in source...")
if os.path.exists(recentfiles):
recent = [f.strip() for f in open(recentfiles).readlines()]
current = [f.strip() for f in open(currentfiles).readlines()]
remove = set([f for f in recent if not f in current])
for f in remove:
try:
os.remove(f)
except IsADirectoryError:
shutil.rmtree(f)
except FileNotFoundError:
pass
if verbose:
print("Removed:", f.split("/")[-1])
if verbose:
print("Done.")
shutil.move(currentfiles, recentfiles)
Come usare
- Copia lo script in un file vuoto, salvalo come
backup_special.py
Cambia -se vuoi- l'opzione dettagliata nell'intestazione dello script:
# --- choose verbose (or not)
verbose = True
# ---
Eseguilo con source e target come argomenti:
python3 /path/to/backup_special.py <source_directory> <target_directory>
Velocità
Ho testato lo script su una directory da 10 GB con circa 40.000 file e directory sul mio drive di rete (NAS), ha fatto il backup praticamente nello stesso momento di rsync.
L'aggiornamento dell'intera directory ha richiesto solo pochi secondi in più rispetto a rsync, su 40.000 file, il che è inaccettabile e nessuna sorpresa, poiché lo script deve confrontare il contenuto con l'ultimo backup eseguito.