rsync: synchroniser les dossiers, mais conserver les fichiers supplémentaires dans la cible

10

Je commence avec rsyncet j'ai essayé de l'utiliser pour garder deux dossiers sur le système local synchronisés. J'ai un dossier source, dont le contenu change au fil du temps (certains fichiers sont ajoutés, certains changements et certains supprimés) et un dossier cible que je veux presque être un miroir de la source. J'ai donc essayé d'utiliser rsync comme ceci:

rsync -a --delete "${source_dir}" "${target_dir}";

Cela garde le contenu de la cible exactement le même que le contenu de la source. Cependant, j'aimerais pouvoir ajouter des fichiers à la cible et non à la source, mais je ne veux pas qu'ils soient supprimés à chaque fois que je fais rsync. D'un autre côté, les fichiers qui étaient auparavant synchronisés puis supprimés dans la source doivent toujours être supprimés.

Existe-t-il un moyen de le faire sans avoir à modifier la commande pour chaque fichier que je souhaite exclure?

Mise à jour : je dois mentionner que je ne suis pas limité à rsync. Si un autre programme fait le travail, c'est bien aussi. J'ai juste essayé de résoudre ce problème en utilisant rsync.

jkrzefski
la source
Bonjour @AszunesHeart, juste curieux, mais avez-vous testé la ou les réponses?
Jacob Vlijm
Avez-vous essayé de supprimer l'option --delete? Celui-ci est comme l'option / MIR dans Robocopy.
SDsolar

Réponses:

9

rsynca une option appelée --exclude-fromoption qui vous permet de créer un fichier contenant une liste de tous les fichiers que vous souhaitez exclure. Vous pouvez mettre à jour ce fichier chaque fois que vous souhaitez ajouter une nouvelle exclusion ou supprimer une ancienne.

Si vous créez le fichier d'exclusion à /home/user/rsync_excludela nouvelle commande serait:

rsync -a --delete --exclude-from="/home/user/rsync_exclude" "${source_dir}" "${target_dir}"

Lors de la création du fichier de liste d'exclusion, vous devez placer chaque règle d'exclusion sur une ligne distincte. Les exclusions sont relatives à votre répertoire source. Si votre /home/user/rsync_excludefichier contient les options suivantes:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Tout fichier ou répertoire appelé secret_filedans votre répertoire source sera exclu.
  • Tous les fichiers dans ${source_dir}/first_dir/subdirseront exclus, mais une version vide de subdirsera synchronisée.
  • Tous les fichiers ${source_dir}/second_diravec un préfixe de common_name.seront ignorés. Alors common_name.txt, common_name.jpgetc.
Arronical
la source
Je ne sais pas si cela fait ce que je voulais. Je trouve également qu'il n'est pas pratique d'énumérer tous les fichiers ou dossiers qui sont ajoutés à la cible. Je préfère avoir un moyen automatique de le faire. Disons que j'ai plusieurs scripts dans la cible qui produisent plusieurs fichiers journaux (également dans la cible) et je ne veux pas répertorier tous les emplacements de ces fichiers dans le fichier rsync_exclude-file. Existe-t-il un moyen de faire en sorte que rsync se souvienne des fichiers synchronisés et de ne laisser ceux-ci être affectés que par --delete?
jkrzefski
Désolé, j'ai mal lu votre question, je pensais que vous vouliez ajouter à la source et que celles-ci n'étaient pas mises à jour pour cibler. Je pense qu'il existe un moyen de faire ce que vous voulez, mais je vais devoir y réfléchir un peu. Je ferai un commentaire une fois que j'aurai eu le temps de le modifier.
Arronical
@jkrzefski Si vous produisez des fichiers à partir d'un autre script dans la cible et que vous souhaitez les exclure de la source, alors pourquoi ne pas changer la destination de ces fichiers journaux dans un autre dossier? Vraisemblablement, si vous ne les synchronisez pas, c'est parce qu'ils sont moins importants.
6

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

entrez la description de l'image ici


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 .recentfilesqui ne sont pas dans .currentfilessont é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 .currentfilesest renommé pour .recentfilesservir 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

  1. Copiez le script dans un fichier vide, enregistrez-le sous backup_special.py
  2. Modifiez -si vous le souhaitez- l'option détaillée dans la tête du script:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. 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.

Jacob Vlijm
la source
Hi @ Aszune'sHeart a ajouté une option scriptée. Veuillez mentionner si tout est clair.
Jacob Vlijm