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

10

Ich fange damit an rsyncund habe versucht, damit zwei Ordner auf dem lokalen System synchron 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 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 exakt mit dem Inhalt der Quelle identisch. 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 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?

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

jkrzefski
quelle
Hallo @AszunesHeart, nur neugierig, aber hast du die Antwort (en) getestet?
Jacob Vlijm
Haben Sie versucht, die Option --delete zu entfernen? Das ist wie die / MIR-Option in Robocopy.
SDsolar

Antworten:

9

rsynchat eine Option namens --exclude-fromOption, mit der Sie eine Datei erstellen können, die eine Liste aller Dateien enthält, die Sie ausschließen möchten. Sie können diese Datei jederzeit aktualisieren, wenn Sie einen neuen Ausschluss hinzufügen oder einen alten entfernen möchten.

Wenn Sie die Ausschlussdatei mit /home/user/rsync_excludedem neuen Befehl erstellen, wäre dies:

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

Wenn Sie die Ausschlusslistendatei erstellen, 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.*
  • Alle secret_filein Ihrem Quellverzeichnis aufgerufenen Dateien oder Verzeichnisse werden ausgeschlossen.
  • Alle Dateien in ${source_dir}/first_dir/subdirwerden ausgeschlossen, aber eine leere Version von subdirwird synchronisiert.
  • Alle Dateien ${source_dir}/second_dirmit dem Präfix von common_name.werden ignoriert. So common_name.txt, common_name.jpgusw.
Arronisch
quelle
Ich bin mir nicht sicher, ob dies das tut, was ich wollte. Außerdem finde ich es unpraktisch, jede Datei oder jeden Ordner aufzulisten, die dem Ziel hinzugefügt werden. Ich hätte lieber eine automatische Möglichkeit, das zu tun. Angenommen, ich habe verschiedene Skripte im Ziel, die mehrere Protokolldateien (auch im Ziel) erzeugen, und ich möchte nicht jeden Speicherort dieser Dateien in der Datei rsync_exclude auflisten. Gibt es eine Möglichkeit, rsync "zu merken", welche Dateien synchronisiert wurden, und nur diejenigen von --delete betroffen zu lassen?
jkrzefski
Entschuldigung, ich habe Ihre Frage falsch verstanden. Ich dachte, Sie wollten sie zur Quelle hinzufügen und haben diese nicht auf das Ziel aktualisiert. Ich denke, es gibt eine Möglichkeit, das zu tun, was Sie wollen, aber ich muss ein bisschen darüber nachdenken. Ich werde einen Kommentar abgeben, sobald ich Zeit zum Bearbeiten hatte.
Arronical
@jkrzefski Wenn Sie Dateien aus einem anderen Skript im Ziel erstellen und diese aus der Quelle ausschließen möchten, ändern Sie das Ziel dieser Protokolldateien in einen anderen Ordner. Wenn Sie sie nicht synchronisieren, liegt dies vermutlich daran, dass sie weniger wichtig sind.
6

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

Skript zum Verwalten 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 (der im Skript festgelegt wird), der den Fortschritt der Sicherung ausgibt (Spiegelung). Sie müssen nicht sagen, dass dies auch zum Protokollieren der Sicherungen verwendet werden kann:

Ausführliche Option

Geben Sie hier die Bildbeschreibung ein


Das Konzept

1. Bei der ersten Sicherung das Skript:

  • 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 und so weiter Backup

  • Das Skript vergleicht die Verzeichnisstruktur und das Änderungsdatum (die Änderungsdaten) der Dateien. Neue Dateien und Verzeichnisse in der Quelle werden in 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(Auflistung der Situation bei der vorherigen Sicherung) mit verglichen .currentfiles. Nur Dateien, aus .recentfilesdenen nicht vorhanden ist, .currentfileswerden offensichtlich aus der Quelle entfernt und 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 die temporäre .currentfilesDatei umbenannt, .recentfilesum den nächsten Sicherungszyklus usw. bereitzustellen.

Das Skript

#!/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 unter 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 etwa 40.000 Dateien und Verzeichnissen auf meinem Netzlaufwerk (NAS) getestet. Die Sicherung wurde fast zur gleichen Zeit wie bei rsync durchgeführt.

Das Aktualisieren des gesamten Verzeichnisses dauerte bei 40.000 Dateien nur wenige Sekunden länger als bei rsync. Dies ist nicht akzeptabel und keine Überraschung, da das Skript den Inhalt mit dem zuletzt erstellten Backup vergleichen muss.

Jacob Vlijm
quelle
Hi @ Aszune'sHeart hat eine Skriptoption hinzugefügt. Bitte erwähnen Sie, wenn alles klar ist.
Jacob Vlijm