Puisque vous avez mentionné: je ne suis pas limité à rsync:
Script pour maintenir le miroir, permettant d'ajouter des fichiers supplémentaires à la cible
Ci-dessous un script qui fait exactement ce que vous décrivez.
Le script peut être exécuté en mode verbeux (à définir dans le script), qui affichera la progression de la sauvegarde (mise en miroir). Inutile de dire que cela peut également être utilisé pour enregistrer les sauvegardes:
Option détaillée
Le concept
1. Lors de la première sauvegarde, le script:
- crée un fichier (dans le répertoire cible), où tous les fichiers et répertoires sont répertoriés;
.recentfiles
- crée une copie exacte (miroir) de tous les fichiers et répertoires du répertoire cible
2. Sur la sauvegarde suivante et ainsi de suite
- Le script compare la structure du répertoire et la ou les dates de modification des fichiers. Les nouveaux fichiers et répertoires de la source sont copiés dans le miroir. En même temps, un deuxième fichier (temporaire) est créé, répertoriant les fichiers et répertoires actuels dans le répertoire source;
.currentfiles
.
- Par la suite,
.recentfiles
(répertoriant la situation lors de la sauvegarde précédente) est comparé à .currentfiles
. Seuls les fichiers .recentfiles
qui ne sont pas dans .currentfiles
sont évidemment supprimés de la source et seront supprimés de la cible.
- Les fichiers que vous avez ajoutés manuellement au dossier cible ne sont en aucun cas "vus" par le script et sont laissés seuls.
- Enfin, le temporaire
.currentfiles
est renommé pour .recentfiles
servir le prochain cycle de sauvegarde et ainsi de suite.
Le scénario
#!/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)
Comment utiliser
- Copiez le script dans un fichier vide, enregistrez-le sous
backup_special.py
Modifiez -si vous le souhaitez- l'option détaillée dans la tête du script:
# --- choose verbose (or not)
verbose = True
# ---
Exécutez-le avec la source et la cible comme arguments:
python3 /path/to/backup_special.py <source_directory> <target_directory>
La vitesse
J'ai testé le script sur un répertoire de 10 Go avec environ 40 000 fichiers et répertoires sur mon lecteur réseau (NAS), il a fait la sauvegarde à peu près en même temps que rsync.
La mise à jour de l'ensemble du répertoire n'a pris que quelques secondes de plus que rsync, sur 40 000 fichiers, ce qui est acceptable et sans surprise, car le script doit comparer le contenu à la dernière sauvegarde effectuée.