rsync und das Rätsel um die Exclude-Option
24. Juni 2008, 20:53 Uhr, Software
Mein absoluter Backup-Liebling ist ja rsync. Sicherlich nicht perfekt, aber für 99.9% der Fälle ist es ein wunderbares und einfaches Werkzeug zum bequemen Sichern von Systemen. Okay, das Logo (das da rechts) ist nicht von dieser Welt. “Form follows function” in die Unendlichkeit gesteigert.
Ansonsten, schöne Software, einfacher geht es eigentlich kaum noch. Bei der Exclude-Option allerdings, die es ermöglicht Dateien oder Verzeichnisse vom Backup auszuschießen, bekommt man gerne mal graue Haare.
Meist beziehen sich die Probleme der Rsync-Einsteiger auf die Anwendung von mehreren Exclude-Optionen um mehrere Ausschluss-Parameter anzugeben. Ein triviales Problem, das schnell zu lösen ist.
Eng mit diesem Problem verbunden, ist aber auch noch ein weiteres Problem, das viele Benutzer zusätzlich frustriert: Manchmal sieht es nämlich so aus, als ob die Exclude-Option gar nicht richtig funktionieren würde.
Ich nenne dieses Problem gerne das absolute Relativpfade-Problem. In rsync(1) wird dieses “Problem” zwar auch erwähnt, aber nur ganz versteckt beantwortet (mehr dazu ganz unten).
Bevor ich jetzt versuche das Problem mit vielen Worten zu beschreiben, male ich es einfach mal mit einem Beispiel auf.
Das Szenario
Stellen wir uns vor, auf dem zu kopierenden Server (im Folgenden straylight genannt) gibt es unter /home/oswald/test diese Datei-Hierarchie:
straylight> find /home/oswald/test -ls drwxr-xr-x 3 oswald 4096 Jun 25 10:05 /home/oswald/test drwxr-xr-x 2 oswald 4096 Jun 25 10:05 /home/oswald/test/haha -rw-r--r-- 1 oswald 0 Jun 25 10:05 /home/oswald/test/haha/hoho -rw-r--r-- 1 oswald 0 Jun 25 10:05 /home/oswald/test/huhu
Dieses Verzeichnis soll kopiert werden, jedoch ohne das Unterverzeichnis haha und dessen Inhalte.
Um das Problem noch ein wenig komplizierter werden zu lassen, gibt es auch noch zwei Varianten in denen es (unterschiedlich) auftritt. Schauen wir uns zunächst die eine Variante an.
Variante 1: Ohne trailing slash
Zunächst nehmen wir einen ganz normaler rsync-Befehl ohne Exclude-Regel und kopieren die komplette Hierarchie auf einen zweiten Rechner (hier freeside genannt):
freeside> rsync -avz -e ssh --delete oswald@straylight:/home/oswald/test /tmp/hihi receiving file list ... done created directory /tmp/hihi test/ test/haha/ test/haha/hoho test/huhu sent 56 bytes received 174 bytes 153.33 bytes/sec total size is 0 speedup is 0.00
Da hier die Quellenangabe nicht auf ein Slash endet, wird auch das Verzeichnis test selbst kopiert:
freeside> find /tmp/hihi -ls drwxr-x--- 3 oswald users 72 Jun 25 10:37 /tmp/hihi drwxr-xr-x 3 oswald users 96 Jun 25 10:05 /tmp/hihi/test drwxr-xr-x 2 oswald users 72 Jun 25 10:05 /tmp/hihi/test/haha -rw-r--r-- 1 oswald users 0 Jun 25 10:05 /tmp/hihi/test/haha/hoho -rw-r--r-- 1 oswald users 0 Jun 25 10:05 /tmp/hihi/test/huhu
Soweit kein Problem. Um die Ausgangslage wiederherzustellen, löschen wir nun das kopierte Verzeichnis wieder.
freeside> rm -rf /tmp/hihi
Jetzt ein rsync mit Exclude-Regel: Das Verzeichnis haha (samt Inhalt hoho) soll nicht kopiert werden. Bei der Exclude-Option geben wir also /home/oswald/test/haha an.
freeside> rsync -avz -e ssh --exclude=/home/oswald/test/haha --delete oswald@straylight:/home/oswald/test /tmp/hihi receiving file list ... done created directory /tmp/hihi test/ test/haha/ test/haha/hoho test/huhu sent 82 bytes received 174 bytes 170.67 bytes/sec total size is 0 speedup is 0.00
An der Ausgabe des Rsync-Befehls sehen wir schon, dass das haha-Verzeichnit trotzdem kopiert wurde. Was lief falsch?
Zunächst löschen wir das kopierte Verzeichnis wieder.
freeside> rm -rf /tmp/hihi
Um das Ganze jetzt abzukürzen, verwenden wir hier gleich die funktionierende, richtige Version und geben bei der Exclude-Option /test/haha an:
freeside> rsync -avz -e ssh --exclude=/test/haha --delete oswald@straylight:/home/oswald/test /tmp/hihi receiving file list ... done created directory /tmp/hihi test/ test/huhu sent 50 bytes received 104 bytes 102.67 bytes/sec total size is 0 speedup is 0.00
Jetzt hat es geklappt. Das haha-Verzeichnis wurde nicht kopiert. Woran liegt’s? Die Pfadangabe bei der Exclude-Option darf nicht absolut, sondern muss quasi-relativ zum Ausgangsverzeichnis der Quellenangabe sein. Wenn ich /home/oswald/test kopiere, dann ist /test/haha die richtige Referenz auf das Verzeichnis /home/oswald/test/haha.
Und damit kommen wir zur 2. Variante dieses Problems.
Zunächst löschen wir das kopierte Verzeichnis wieder.
freeside> rm -rf /tmp/hihi
Variante 2: Mit trailing slash
Für Unix-Befehle ist es immer ein großer Unterschied, ob man Verzeichnisse mit oder ohne einem trailing slash adressiert. Um den Unterschied in diesem Fall zu zeigen, rufen wir den Ausgangs-Rsync-Befehl nochmals auf. Nur dieses Mal mit einem angehängten Slash.
freeside> rsync -avz -e ssh --delete oswald@straylight:/home/oswald/test/ /tmp/hihi receiving file list ... done created directory /tmp/hihi ./ haha/ haha/hoho huhu sent 56 bytes received 169 bytes 150.00 bytes/sec total size is 0 speedup is 0.00
Da hier die Quellenangabe nun mit einem Slash endet, wird der Inhalt von test kopiert und nicht das Verzeichnis test selbst:
freeside> find /tmp/hihi -ls 693847 0 drwxr-xr-x 3 oswald users 96 Jun 25 10:05 /tmp/hihi 693925 0 drwxr-xr-x 2 oswald users 72 Jun 25 10:05 /tmp/hihi/haha 693988 0 -rw-r--r-- 1 oswald users 0 Jun 25 10:05 /tmp/hihi/haha/hoho 693989 0 -rw-r--r-- 1 oswald users 0 Jun 25 10:05 /tmp/hihi/huhu
Zunächst löschen wir das kopierte Verzeichnis erneut.
freeside> rm -rf /tmp/hihi
Um in diesem Fall das Verzeichnis /home/oswald/test/haha vom Kopieren auszuschiessen, muss bei der Exclude-Option /haha angegeben werden:
freeside> rsync -avz -e ssh --exclude=/haha --delete oswald@straylight:/home/oswald/test/ /tmp/hihi receiving file list ... done created directory /tmp/hihi ./ huhu sent 45 bytes received 99 bytes 96.00 bytes/sec total size is 0 speedup is 0.00
Wunderbar, haha und sein Inhalt hoho wurden nicht kopiert.
Die Erklärung
Eigentlich ist es ganz einfach: Die Angabe bei der Exclude-Option ist ein Pattern und kein Verzeichnis. Und dieses Pattern wird auf das, was da kopiert wird, angewendet. Beim Kopieren wird nicht der absolute sondern nur der relative Pfad kopiert und eben diesen Pfad muss das Pattern matchen. Der Slash am Anfang von /haha hat nichts mit dem Root-Verzeichnis eines Dateisystems zu tun und bezieht sich nur auf den kopierenden Pfad.
Im Manual rsync(1) wird dazu der Begriff ‘root of the transfer’ eingeführt und so erkärt:
If the pattern starts with a / then it is anchored to a particular spot in the hierarchy of files, otherwise it is matched against the end of the pathname. This is similar to a leading ^ in regular expressions. Thus ‘/foo’ would match a file named ‘foo’ at either the ‘root of the transfer’ (for a global rule) or in the merge-file’s directory (for a per-directory rule).
Kurzum: Die Exclude-Option bezieht sich auf das ‘root of the transfer’.
Lässt man das Slash am Anfang weg und schreibt also anstelle von /haha nun haha, dann werden komplett unabhägig vom Pfad alle Verzeichnisse und Dateien, die genau haha heißen, ignoriert.
Zusätzlich kann man dann natürlich auch noch die üblichen Unix-Shell-Wildcards * und ? in diesem Pattern verwenden. Aber das ist eine andere Geschichte und die ist auch wieder ganz trivial.


Maex