rsync: Ordner synchronisieren, aber zusätzliche Dateien im Ziel behalten

rsync: Ordner synchronisieren, aber zusätzliche Dateien im Ziel behalten

Ich bin gerade dabei, damit anzufangen rsyncund habe versucht, damit zwei Ordner auf dem lokalen System synchronisiert zu halten. Ich habe einen Quellordner, dessen Inhalt sich im Laufe der Zeit ändert (einige Dateien werden hinzugefügt, einige geändert und einige gelöscht) und einen Zielordner, der fast ein Spiegelbild der Quelle sein soll. Also habe ich versucht, rsync wie folgt zu verwenden:

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

Dadurch bleibt der Inhalt des Ziels genau derselbe wie der Inhalt der Quelle. Ich möchte jedoch einige Dateien zum Ziel und nicht zur Quelle hinzufügen können, möchte aber nicht, dass sie jedes Mal gelöscht werden, wenn ich rsync ausführe. Andererseits sollten Dateien, die früher synchronisiert und dann in der Quelle gelöscht wurden, weiterhin gelöscht werden.

Gibt es eine Möglichkeit, dies zu tun, ohne den Befehl für jede Datei ändern zu müssen, die ich ausschließen möchte?

Aktualisieren: Ich sollte erwähnen, dass ich nicht auf rsync beschränkt bin. Wenn ein anderes Programm die Arbeit erledigt, ist das auch in Ordnung. Ich habe gerade versucht, dies mit rsync zu lösen.

Antwort1

rsyncverfügt über eine Option namens --exclude-fromOption, mit der Sie eine Datei mit einer Liste aller Dateien erstellen können, die Sie ausschließen möchten. Sie können diese Datei aktualisieren, wenn Sie eine neue Ausnahme hinzufügen oder eine alte entfernen möchten.

Wenn Sie die Ausschlussdatei erstellen, /home/user/rsync_excludelautet der neue Befehl:

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

Beim Erstellen der Ausschlusslistendatei sollten Sie jede Ausschlussregel in eine separate Zeile setzen. Die Ausschlüsse beziehen sich auf Ihr Quellverzeichnis. Wenn Ihre /home/user/rsync_excludeDatei die folgenden Optionen enthielt:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • secret_fileAlle in Ihrem Quellverzeichnis aufgerufenen Dateien oder Verzeichnisse werden ausgeschlossen.
  • Alle Dateien in ${source_dir}/first_dir/subdirwerden ausgeschlossen, aber eine leere Version subdirwird synchronisiert.
  • Alle Dateien ${source_dir}/second_dirmit dem Präfix common_name.werden ignoriert. common_name.txtAlso common_name.jpgusw.

Antwort2

Da Sie erwähnt haben:Ich bin nicht auf rsync beschränkt:

Skript zur Pflege des Spiegels, das das Hinzufügen zusätzlicher Dateien zum Ziel ermöglicht

Nachfolgend ein Skript, das genau das macht, was Sie beschreiben.

Das Skript kann ausgeführt werden inausführlichModus (wird im Skript festgelegt), der den Fortschritt der Sicherung (Spiegelung) ausgibt. Natürlich kann dies auch zum Protokollieren der Sicherungen verwendet werden:

Ausführliche Option

Bildbeschreibung hier eingeben


Das Konzept

1. Beim ersten Backup führt das Skript Folgendes aus:

  • erstellt eine Datei (im Zielverzeichnis), in der alle Dateien und Verzeichnisse aufgelistet sind;.recentfiles
  • erstellt eine exakte Kopie (Spiegel) aller Dateien und Verzeichnisse im Zielverzeichnis

2. Beim nächsten und so weiter Backup

  • Das Skript vergleicht Verzeichnisstruktur und Änderungsdatum(e) der Dateien. Neue Dateien und Verzeichnisse in der Quelle werden auf den Spiegel kopiert. Gleichzeitig wird eine zweite (temporäre) Datei erstellt, die die aktuellen Dateien und Verzeichnisse im Quellverzeichnis auflistet .currentfiles.
  • Anschließend .recentfileswird (Auflistung der Situation beim vorherigen Backup) mit verglichen .currentfiles.NurDateien, .recentfilesdie nicht darin enthalten sind .currentfiles, werden offensichtlich aus der Quelle entfernt und auch aus dem Ziel.
  • Dateien, die Sie manuell zum Zielordner hinzugefügt haben, werden vom Skript in keiner Weise „gesehen“ und bleiben unverändert.
  • Abschließend .currentfileswird das temporäre Laufwerk umbenannt, um .recentfilesfür den nächsten Sicherungszyklus verwendet zu werden, und so weiter.

Das Drehbuch

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

Wie benutzt man

  1. Kopieren Sie das Skript in eine leere Datei und speichern Sie es unterbackup_special.py
  2. Ändern Sie – falls gewünscht – die ausführliche Option im Kopf des Skripts:

    # --- choose verbose (or not)
    verbose = True
    # ---
    
  3. Führen Sie es mit Quelle und Ziel als Argumente aus:

     python3 /path/to/backup_special.py <source_directory> <target_directory>
    

Geschwindigkeit

Ich habe das Skript an einem 10 GB großen Verzeichnis mit etwa 40.000 Dateien und Verzeichnissen auf meinem Netzwerklaufwerk (NAS) getestet. Es hat das Backup in ungefähr derselben Zeit erstellt wie rsync.

AktualisierungDas gesamte Verzeichnis benötigte bei 40.000 Dateien nur ein paar Sekunden länger als rsync, was meiner Meinung nach akzeptabel und keine Überraschung ist, da das Skript den Inhalt mit der zuletzt erstellten Sicherung vergleichen muss.

verwandte Informationen