it-swarm.com.de

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

Ich fange mit rsync an und versuche, damit zwei Ordner auf dem lokalen System synchron bleiben. Ich habe einen Quellordner, dessen Inhalt sich im Laufe der Zeit ändert (einige Dateien werden hinzugefügt, einige werden geändert und einige werden gelöscht), und einen Zielordner, der beinahe ein Spiegel 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 genauso wie der Inhalt der Quelle. Ich möchte jedoch in der Lage sein, einige Dateien zum Ziel und nicht zur Quelle hinzuzufügen, aber ich möchte nicht, dass sie jedes Mal gelöscht werden, wenn ich rsync mache. Auf der anderen Seite sollten Dateien, die zuvor 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?

pdate: 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.

10
jkrzefski

rsync hat eine Option namens --exclude-from, 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 einen neuen Ausschluss hinzufügen oder einen alten entfernen möchten.

Wenn Sie die Ausschlussdatei unter /home/user/rsync_exclude erstellen, lautet 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_exclude -Datei die folgenden Optionen enthielt:

secret_file
first_dir/subdir/*
second_dir/common_name.*
  • Alle Dateien oder Verzeichnisse mit dem Namen secret_file in Ihrem Quellverzeichnis werden ausgeschlossen.
  • Alle Dateien in ${source_dir}/first_dir/subdir werden ausgeschlossen, aber eine leere Version von subdir wird synchronisiert.
  • Alle Dateien in ${source_dir}/second_dir mit dem Präfix common_name. werden ignoriert. Also common_name.txt, common_name.jpg usw.
9
Arronical

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

Skript zur Verwaltung des Spiegels, mit dem zusätzliche Dateien zum Ziel hinzugefügt werden können

Unten ein Skript, das genau das tut, was Sie beschreiben.

Das Skript kann im ausführlichen Modus ausgeführt werden (im Skript festzulegen), der den Fortschritt der Sicherung (Spiegelung) ausgibt. Dies kann auch zum Protokollieren der Sicherungen verwendet werden:

Ausführliche Option

enter image description here


Das Konzept

1. Bei der ersten Sicherung führt das Skript Folgendes aus:

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

2. Beim nächsten usw. Backup

  • Das Skript vergleicht die Verzeichnisstruktur und das Änderungsdatum der Dateien. Neue Dateien und Verzeichnisse in der Quelle werden auf den Spiegel kopiert. Gleichzeitig wird eine zweite (temporäre) Datei erstellt, in der die aktuellen Dateien und Verzeichnisse im Quellverzeichnis aufgelistet sind. .currentfiles.
  • Anschließend wird .recentfiles (Auflisten der Situation bei der vorherigen Sicherung) mit .currentfiles verglichen. Nur Dateien aus .recentfiles, die nicht in .currentfiles enthalten sind, werden offensichtlich entfernt von der Quelle und wird vom Ziel entfernt.
  • Dateien, die Sie manuell zum Zielordner hinzugefügt haben, werden vom Skript ohnehin nicht "gesehen" und bleiben in Ruhe.
  • Schließlich wird der temporäre .currentfiles in .recentfiles umbenannt, um den nächsten Sicherungszyklus zu bedienen 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 als backup_special.py
  2. Ändern Sie, wenn Sie möchten, 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 in einem 10-GB-Verzeichnis mit ca. 40.000 Dateien und Verzeichnissen auf meinem Netzwerklaufwerk (NAS) getestet. Es hat die Sicherung fast zur gleichen Zeit wie rsync durchgeführt.

Das Aktualisieren des gesamten Verzeichnisses auf 40.000 Dateien dauerte nur ein paar Sekunden länger als rsync, was nicht verwunderlich ist, da das Skript den Inhalt mit der zuletzt erstellten Sicherung vergleichen muss .

6
Jacob Vlijm