Dateien vergleichen in PowerShell

    Dateien vergleichen mit PowerShellPowerShell bietet mehrere Möglichkeiten, um Dateien auf ihre Gleichheit bzw. auf Ab­weichungen zu unter­suchen. Abhängig davon, ob man mit der bloßen Information zufrieden ist, dass zwei Dateien (nicht) identisch sind oder ob man die Unterschiede im Einzelnen sehen möchte, verwendet man Get-FileHash oder Compare-Object.

    Das Vergleichen von Dateien gehört zu den gängigen Aufgaben in der Systemverwaltung und entsprechend bietet Windows dafür schon lange zwei Kommandozeilen-Tools an. Es handelt sich dabei um comp.exe und fc.exe, die man auch in den Zeiten von PowerShell nicht abschreiben sollte. Gerade fc.exe bietet mehr Optionen als das Cmdlet Compare-Object, so dass man weiterhin darauf ausweichen wird, wenn man sich etwa die Zeilennummern anzeigen lassen will.

    Identität mittels Hash-Wert prüfen

    Häufig will man nur feststellen, ob es sich bei zwei Dateien um Dubletten handelt, so dass man etwa eine davon löschen kann. In diesem Fall interessieren eventuelle Unterschiede nicht im Detail, es reicht zu wissen, ob die Dateien identisch sind. Hier empfiehlt sich bei großen oder bei binären Dateien das Berechnen eines Hash-Werts, weil dies schneller geht als der Vergleich der Dateiinhalte. PowerShell bietet für diese Aufgabe das Cmdlet Get-FileHash, dessen Ergebnisse man auf Übereinstimmung prüft:

    ((Get-FileHash ".\datei1.xml").hash) -eq ((Get-FileHash ".\datei2.xml").hash)

    Dieses Beispiel ruft Get-FileHash mit jeweils einem Dateinamen auf und vergleicht die Eigenschaft hash mit Hilfe des -eq-Operators. Sind die beiden Dateien identisch, dann ist das Ergebnis True, ansonsten False.

    Der Vergleich von Dateien über Hash-Werte ergibt nur True oder False.

    Compare-Object liest Dateiinhalte nicht ein

    Begnügt man sich nicht mit dieser Information, sondern will die Unterschiede im Detail sehen, dann muss man Compare-Object (Alias compare) einsetzen. Wie der Name des Cmdlets schon nahelegt, vergleicht es nicht bloß Dateien, sondern alle möglichen Objekte. Entsprechend ist es nicht dafür ausgelegt, eigenständig Dateien auszulesen und auf Unterschiede zu prüfen. Vielmehr muss man ihren Inhalt selbst extrahieren und dann an Compare-Object übergeben. Dafür bietet sich Get-Content an:

    compare -ReferenceObject (Get-Content ".\test.xml") -DifferenceObject (Get-Content ".\test1.xml")

    Die Parameter ReferenceObject und DifferenceObject bezeichnen das ursprüngliche sowie das damit zu vergleichende Objekt. Die Namen der Parameter können weggelassen werden, wenn sie an 1. und 2. Position stehen. Wie im obigen Beispiel muss der Aufruf von Get-Content aber in Klammern gesetzt werden:

    compare (Get-Content ".\test.xml") (Get-Content ".\test1.xml")

    Inverser Vergleich, Groß- und Kleinschreibung

    Der Vergleich durch Compare-Object lässt sich anhand einiger Parameter modifizieren. So beachtet es bei Angabe von -CaseSensitive Groß- und Kleinschreibung, während es diese standardmäßig ignoriert.

    Standardmäßig zeigt Compare-Object nur die Unterschiede, aber man kann sich auch die Übereinstimmungen ausgeben lassen.

    Gibt man -IncludeEqual an, dann werden alle Zeilen beider Dateien aufgelistet und der so genannte Side Indicator zeigt, ob sie gleich sind oder ob eine davon nur in der ersten oder in der zweiten Datei vorkommt. Ergänzt man schließlich -IncludeEqual um -ExcludeDifferent, dann erhält man nur jene Zeilen, die in beiden Dateien gleich sind.

    Modifikation der Inhalte mit RegEx

    Die relativ spärlichen Optionen von Compare-Object kann man dadurch kompensieren, dass man den Inhalt der eingelesenen Dateien mit Hilfe des replace-Operators und regulären Ausdrücken für den Vergleich aufbereitet. So könnte man zum Beispiel die Fähigkeit von fc.exe, Tabs und Leerzeichen zu komprimieren, folgendermaßen nachbilden:

    compare ((Get-Content ".\test.xml") -replace "(\s)+",'$1') ((Get-Content ".\test1.xml") -replace "(\s)+",'$1')

    In diesem Beispiel werden alle Whitespace-Zeichen (repräsentiert durch \s), die mehrfach hintereinander auftreten, auf ein Exemplar zusammengestaucht. Bei komplexeren RegEx-Ausdrücken wäre zu überlegen, ob man das Ergebnis der beiden Aufrufe von Get-Content in einer Variablen speichert und diese dann an Compare-Object übergibt. Das würde die Übersichtlichkeit des Codes verbessern.

    Keine Kommentare