Wie funktioniert diff?

Mit dem Linux/Unix-Befehl „diff“ lassen sich Dateien sehr schnell auf Änderungen prüfen. Die man page von diff ist allerdings wenig hilfreich, wenn man sich näher mit dem Programm beschäftigen möchte. Kommentarlos werden Zeilen wie „1,2d0“ ausgespuckt, ohne dass diese irgendwo näher erklärt würden. Hier also der Versuch, Licht ins Dunkel zu bringen. So schwer ist das nämlich gar nicht

Zunächst erstellen wir einmal zwei relativ einfache Dateien, die wir vergleichen wollen. Sie sehen so aus:

1.txt:
a
b
c
d
e

2.txt:
a
b
c
d
e
f

Um mal schnell zu sehen, was diff überhaupt so kann, tippen wir den Befehl mit den beiden Dateien als Parameter ein:

diff 1.txt 2.txt

Diff vergleicht die beiden Dateien und gibt nur die Unterschiede (Hunks) auf der Konsole aus. Zeilen, die in beiden Dateien gleich sind, werden nicht ausgegeben. Das Ergebnis sieht für obigen Aufruf so aus:

5a6
> f

Diff zeigt uns hier an, dass rechts in Zeile 6 das „f“ zusätzlich enthalten ist (a für „added“) und links nach Zeile 5 fehlt. Da Diff normalerweise in erster Linie dazu verwendet wird, Programme zu „patchen“ (also eine gewisse Menge an Text aus einer „Patch“-Datei in einen Quelltext an der richtigen Stelle einzufügen), liest sich die Ausgabe so: „Füge auf der linken Seite, nach Zeile 5, in Zeile 6 ein „f“ ein, um die linke Datei auf den Stand der rechten zu bringen.“

Machen wir schnell die Gegenprobe und tauschen die beiden Parameter (Dateien) aus:

diff 2.txt 1.txt
6d5
< f

Jetzt sagt uns diff, dass links die Zeile 6 das „f“ gelöscht werden muss (d für „delete“), um auf den Stand der rechten Datei zu kommen. Es kommt also darauf an, in welcher Reihenfolge man die Dateien angibt, die man vergleichen möchte. Treiben wir das Spielchen etwas weiter und ändern in Datei 1.txt die zweite Zeile. Die Datei sieht danach so aus:

1.txt: 
a 
bb 
c 
d 
e

Was kommt nun heraus, wenn wir die Zeilen (wieder in der ursprünglichen Reihenfolge) vergleichen?

diff 1.txt 2.txt 
2c2 
< bb
---
> b
5a6
> f

Diff hat nun erkannt, dass sich die Dateien auf beiden Seiten in Zeile 2 unterscheiden (2c2). Das c steht in diesem Falle für „change“. Gleich danach listet es auf, was in der linken Datei in Zeile 2 zu finden ist (bb) und darunter (getrennt durch drei Striche) das, was rechts enthalten ist (b). Der Rest ist bekannt.

Was passiert, wenn wir nun noch eine zusätzliche Zeile in der Datei 2.txt einfügen? Hier die Datei nach der Änderung:

2.txt:
a
b
c
d
d2
e
f

Probieren wir’s aus:

diff 1.txt 2.txt
2c2
< bb --- > b
4a5
> d2
5a7
> f

Hier sieht diff, dass rechts eine neue Zeile hinzugekommen ist (a für „add“), und zwar Zeile 5 (links käme sie nach Zeile 4) und der Inhalt „d2“ ist.Diff ist so intelligent, dass es erkennt, dass sich in beiden Dateien in der Zeile mit dem „e“ nichts geändert hat und verweist folgerichtig auf Zeile 7 rechts (mit dem „f“), die links nach Zeile 5 zu kommen hätte.

Soweit verstanden? Gut! Ab jetzt wird’s übersichtlicher:

diff --side-by-side 1.txt 2.txt
a a
bb | b
c c
d d
> d2
e e
> f

Für kleinere Dateien ist diese Darstellung sicherlich vorteilhaft, für komplette Apache-Konfigurationsdateien aber wohl kaum praktikabel. Es sei denn, man benutzt less und blättert Seite für Seite durch die Ausgabe durch. Zum Glück gibt es den Parameter –suppress-common-lines. Hierzu ein Beispiel aus der Praxis:

diff --side-by-side --suppress-common-lines httpd.conf httpd.conf.10.10.2005
DocumentRoot "/etc/httpd/html" | DocumentRoot "/html/httpd/html"

Hier hat also irgendjemand den Pfad zur DocumentRoot unseres Webservers seit dem letzten Backup am 10.10. 2005 geändert. Das Gleiche noch mal anders dargestellt:

diff -c1 httpd.conf httpd.conf.10.10.2005
*** httpd.conf Tue Oct 11 16:05:38 2005
--- httpd.conf.10.10.2005 Tue Oct 10 18:32:40 2005
********
**** 47,49 ****
UseCanonicalName Off
! DocumentRoot "/home/httpd/html"
#
---- 47,49 ----
UseCanonicalName Off
! DocumentRoot "/etc/httpd/html"
#

Der Befehl besagt: „Zeige alle unterschiedlichen Zeilen der Dateien und hänge 1 Zeile aus dem Kontext (-c1) vor und nach der Ausgabe der Fundstellen an!“ Das kann hilfreich sein, um die Änderungen in der Datei auch zu finden. Zunächst zeigt die Ausgabe beide zu vergleichenden Dateien mit ihren Datumsangaben an. Danach folgt die Ausgabe, welche Zeilen dargestellt werden (47,49), in diesem Falle jeweils 47-49 und dann beginnt die Ausgabe der Fundstellen für jede der beiden Dateien untereinander.

Hier noch die Ausgabe des „unified“ Formates (-u), wie es tatsächlich für Patchdateien verwendet wird. Auch hier lässt sich die Anzahl der Kontextzeilen wieder vorgeben (ohne Angabe werden jeweils 3 Zeilen vor und nach der Fundstelle benutzt):

diff -u1 httpd.conf httpd.conf.10.10.2005
--- httpd.conf 2005-10-11 16:05:38.000000000 +0200

httpd.conf.10.10.2005 2005-10-10 18:32:40.000000000 +0200 _
@@ -47,3 +47,3 @@
UseCanonicalName Off
-DocumentRoot "/home/httpd/html"
+DocumentRoot "/etc/httpd/html"
#

Die Zeile mit den beiden „@“ enthält nun Angaben für das Programm „patch“ und die Ausgabe fällt deutlich kompakter aus. Die Zahlen besagen, dass aus dem Kontext 3 Zeilen ausgegeben werden (-u1: 1 Zeile vor und nach der Fundstelle, plus die Fundstelle selbst) und dass die Ausgabe ab Zeile 47 beginnt. Dass tatsächlich 4 Zeilen ausgegeben werden liegt daran, dass ja auch beide geänderte Zeilen auf der Konsole angezeigt werden (die, die mit „-“ und „+“ beginnen). Hätten wir -u ohne Zahlenangabe benutzt, stünde dort

@@ -45,7 +45,7 @@

Was das bedeutet, sollte nun klar sein.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.