
Ich habe einen rsync
Prozess, der Inhalte aus einem Quell-Repository (das einer Versionskontrolle unterliegt) in ein gemeinsam genutztes NFS-Mount synchronisiert.
Das Szenario (so schlimm es auch ist) ist, dass der Zielordner mehr Inhalt enthält als der Quellordner, weil andere Inhalte aus anderen Quellen mit dem Zielordner synchronisiert werden. Ordnerstrukturen können beispielsweise so aussehen:
Quelle
a/a1.txt
a/a2.txt
b/b1.txt
Ziel
a/a1.txt
a/a2.txt
a/a3.txt
b/b1.txt
c/c1.txt
(in diesem Beispiel a/a3.txt
und c/c1.txt
werden von woanders mit dem Ziel synchronisiert. In der Praxis handelt es sich dabei um mehrere andere Quellen und die Inhalte/Prozesse für diese können nicht beeinflusst werden.)
Nehmen wir nun an, dass der Quellordner die a/a2.txt
Datei löscht. Mit dem vorhandenen Setup würde diese Datei im Ziel nicht gelöscht werden. Die Verwendung von --delete
würde jedoch dazu führen, dass andere Dateien gelöscht werden, und dies darf nicht geschehen.
Wie kann --delete
ich rsync verwenden und trotzdem die Anforderungen erfüllen? Da das Quellverzeichnis einer Versionskontrolle unterliegt, ist es ganz einfach, ein Vorher-Nachher-Bild dieses Verzeichnisses zu erhalten. So könnte ein differenzielles Backup unter Verwendung des ursprünglichen Quellverzeichnisses als Referenz berechnet werden. Aber ist das die beste Methode?
Antwort1
So können Sie es nicht verwenden rsync --delete
. Es ist zustandslos und speichert keine Aufzeichnungen darüber, welche Dateien zwischen den Durchläufen gelöscht wurden. Das --delete
Flag weist lediglich an, rsync
alle Dateien am Ziel zu löschen, die in der Quelle nicht vorhanden sind.
Um diese eingeschränkte Löschung zu implementieren, müssen Sie meines Erachtens Ihren eigenen Status beibehalten. Weder rsync
noch unison
können dies für Sie tun.
Das Folgende ist keine vollständig fehlersichere Lösung; es ist ein Ausgangspunkt. (Es verarbeitet jedoch Dateien mit seltsamen Namen – einschließlich solcher, die eine eingebettete neue Zeile enthalten.)
Nehmen wir zwei Verzeichnisse src
und an dst
. (Für die Zwecke des Beispiels spielt es keine Rolle, ob dst
lokal oder remote.)
# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z > .state.src
Führen Sie bei jeder Sicherung den folgenden Code aus
# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/
# Determine the set of files to delete in "dst/"
( cd src && find . -type f -print0 ) | LC_ALL=C sort -z | tee .state.src.new |
LC_ALL=C comm -z - -13 .state.src |
ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'
# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src
Wenn Ihre Version comm
(wie meine) älter als GNU Coreutils 8.25 ist und das Flag nicht hat -z
, können Sie diesen alternativen Workaround verwenden:
# Find the current list of files (do this just once, to prep the cache)
( cd src && find . -type f -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort > .state.src
Führen Sie bei jeder Sicherung den folgenden Code aus
# Run the rsync to transfer files. "dst/" could be local
rsync -av src/ remote:dst/
# Determine the set of files to delete in "dst/"
( cd src && find . -type f -print0 ) | tr '\0\n' '\n\0' | LC_ALL=C sort | tee .state.src.new |
LC_ALL=C comm -13 - .state.src |
tr '\0\n' '\n\0' |
ssh remote 'while IFS= read -d "" -r f; do rm -f "dst/$f"; done'
# That seemed to work, so update the state cache
[[ 0 -eq $? ]] && mv -f .state.src.new .state.src