Vergleichen Sie zwei Verzeichnisse rekursiv mit diff -r ohne Ausgabe auf defekten Links

38

Ich verwende diff -r a b, um Verzeichnisse a und b rekursiv zu vergleichen . Es kommt jedoch häufig vor, dass es einige fehlerhafte Links gibt (dieselben fehlerhaften Links in den Verzeichnissen a und b und Verweise auf dieselben, nicht vorhandenen Ziele).

diff gibt dann Fehlermeldungen für diese Fälle aus und beendet das Programm mit einem Exit-Code ungleich Null. Ich möchte jedoch, dass es still bleibt und beendet das Programm mit 0, da die Verzeichnisse in meinem Buch dieselben sind.

Wie kann ich das machen?

Marcus Junius Brutus
quelle
Möchten Sie immer noch die Symlinks vergleichen (und als gleichwertig, aber fehlerhaft identifiziert), oder ist es akzeptabel, alle Symlinks zu ignorieren, wenn Sie diesen Unterschied machen?
ire_and_curses
verglichen und als gleichwertig identifiziert, ist es mir egal, ob sie kaputt sind. Ich versuche nur zu überprüfen, ob mein Rsync funktioniert hat.
Marcus Junius Brutus

Antworten:

24

Für Version 3.3 oder höher diffsollten Sie die --no-dereferenceOption verwenden, wie in der Antwort von Pete Harlan beschrieben .

Leider unterstützen ältere Versionen von diff nicht das Ignorieren von Symlinks :

Einige Dateien sind weder Verzeichnisse noch reguläre Dateien: Es handelt sich um ungewöhnliche Dateien wie symbolische Links, spezielle Gerätedateien, Named Pipes und Sockets. Derzeit werden diffsymbolische Links wie normale Dateien behandelt. Andere spezielle Dateien werden wie normale Dateien behandelt, wenn sie auf der obersten Ebene angegeben sind. Beim Vergleich von Verzeichnissen wird lediglich das Vorhandensein dieser Dateien gemeldet. Dies bedeutet, dass patchÄnderungen an solchen Dateien nicht dargestellt werden können. Wenn Sie beispielsweise ändern, auf welche Datei eine symbolische Verknüpfung verweist diff, wird anstelle der Änderung der symbolischen Verknüpfung die Differenz zwischen den beiden Dateien ausgegeben.

diffsollte optional Änderungen an speziellen Dateien speziell melden und patchsollte erweitert werden, um diese Erweiterungen zu verstehen.

Wenn Sie nur eine rsync-Überprüfung durchführen möchten (und vermutlich die fehlenden Informationen korrigieren möchten), können Sie den Befehl rsync einfach ein zweites Mal ausführen. Wenn Sie dies nicht möchten, kann es ausreichend sein, das Verzeichnis zu überprüfen .

Wenn Sie dies wirklich tun möchten, diffkönnen Sie finddie Symlinks überspringen und diff für jede Datei einzeln ausführen. Übergeben Sie die Verzeichnisse a und b als Argumente:

#!/bin/bash
# Skip files in $1 which are symlinks
for f in `find $1/* ! -type l`
do
    # Suppress details of differences
    diff -rq $f $2/${f##*/}
done

oder als Einzeiler:

for f in `find a/* ! -type l`;do diff -rq $f b/${f##*/};done

Dadurch werden Dateien identifiziert, die sich im Inhalt unterscheiden, oder Dateien, die sich in a, aber nicht in b befinden .

Beachten Sie, dass:

  • Da wir Symlinks vollständig überspringen, wird dies nicht bemerken, wenn in b keine Symlink-Namen vorhanden sind . Wenn Sie dies benötigen, benötigen Sie einen zweiten Find-Pass, um alle Symlinks zu identifizieren, und überprüfen Sie dann explizit, ob sie in b vorhanden sind .
  • Zusätzliche Dateien in b werden nicht identifiziert, da die Liste aus dem Inhalt von a besteht . Dies ist wahrscheinlich kein Problem für Ihr rsyncSzenario.
ire_and_curses
quelle
Das vorgeschlagene Skript funktioniert nicht rekursiv für Verzeichnisse in Verzeichnis 'a' (die mit b / $ {f ## *} für 'b' erstellten Pfade sind nicht korrekt).
Marcus Junius Brutus
@MarcusJuniusBrutus - Ja, du hast recht. Ich denke, die Lösung ist, ein # zu entfernen, zB for f in ein / * zu finden! -Typ l ;do echo $f b/${f#*/};done. Ich habe momentan jedoch keine Zeit, dies zu testen. Lassen Sie mich wissen, ob das funktioniert.
ire_and_curses
Es ist besser, aber es bringt die Dateipfade in vielen Fällen durcheinander. Das Skript (mit einem # entfernt) muss anscheinend von einem Verzeichnis direkt über 'a' aufgerufen werden, um zu funktionieren.
Marcus Junius Brutus
Diese Antwort wird obsolet, wenn Sie GNU diff 3.3 verwenden (siehe Postings unten)
Bernd Gloss
Das obige Skript weist mehrere Probleme auf, da zuerst alle Dateinamen gefunden und an eine erweiterte Befehlszeile übergeben werden. (1) Es wird nur mit kleinen Sammlungen von Dateien funktionieren, da es. (2) Dateinamen mit Sonderzeichen (auch Leerzeichen) werden nicht verarbeitet. (3) Immer $(xxx)anstelle von Backticks verwenden. Die Symmetrie der Backticks macht sie weniger lesbar und verhindert das Verschachteln. Zu 1 und 2 siehe stackoverflow.com/questions/11366184/…
Stéphane Gourichon
19

Seit Version 3.3 diffunterstützt GNU keine Dereferenzierung von Symlinks, vergleicht dann aber die Pfade, auf die sie zeigen.

Installieren Sie GNU diffutils> = 3.3 und verwenden Sie die --no-dereferenceOption; Dafür gibt es keine kurze Option.

Die Diagnose ist stumm, wenn gleich oder:

Symbolische Verknüpfungen /tmp/noderef/a/symlinkund /tmp/noderef/b/symlinkUnterschiede

Philippe De Muyter
quelle
Nun, wenn es nur die inhaltlichen Änderungen zeigen würde, als ob der Symlink eine reguläre Datei wäre ...: - /
lindes
6

Sie können eine neuere Version von verwenden diff

In diffGNU diffutils3.3 ist eine --no-dereferenceOption enthalten, mit der Sie die Symlinks selbst und nicht ihre Ziele vergleichen können. Es meldet, wenn sie sich unterscheiden, ist leise, wenn sie sich einig sind, und es ist ihnen egal, ob sie kaputt sind.

Ich weiß nicht, wann die Option hinzugefügt wurde. es ist in 2.8.1 nicht vorhanden.

Pete Harlan
quelle
Ich kann bestätigen, dass es in diff (GNU diffutils) 3.2 auch nicht gibt
Elder Geek