43 Stimmen

Rekursiv vergleichen Sie zwei Verzeichnisse, um sicherzustellen, dass sie dieselben Dateien und Unterverzeichnisse haben

Von dem, was ich beobachte, ist filecmp.dircmp rekursiv, aber unzureichend für meine Bedürfnisse, zumindest in py2. Ich möchte zwei Verzeichnisse und alle enthaltenen Dateien vergleichen. Existiert das, oder muss ich es aufbauen (zum Beispiel mit os.walk). Ich bevorzuge vorgefertigte Lösungen, bei denen bereits jemand anderes das Einheitstesten durchgeführt hat :)

Der tatsächliche 'Vergleich' kann nachlässig sein (zum Beispiel Berechtigungen ignorieren), wenn das hilft.

Ich hätte gerne etwas Boolesches, und report_full_closure ist ein gedruckter Bericht. Es geht auch nur in gemeinsame Unterverzeichnisse. AFIAC, wenn sie etwas im linken oder rechten Verzeichnis haben, sind nur diese unterschiedlichen Verzeichnisse. Ich baue dies stattdessen mit os.walk.

0 Stimmen

Was ist "AFIAC"? Kann es nicht in einem gängigen Akronym finden, wenn gesucht wird.

0 Stimmen

@LondonAppDev Meine Vermutung ist, dass der Autor AFAIC gemeint hat (so weit ich das beurteilen kann (z.B. oxfordlearnersdictionaries.com/us/definition/english/…)), aber es möglicherweise als AFIAC falsch geschrieben hat. Eng verwandt mit AFAIK, d.h. so weit ich weiß.

35voto

Mateusz Kobos Punkte 601

Hier ist eine alternative Implementierung der Vergleichsfunktion mit dem filecmp-Modul. Es verwendet eine Rekursion anstelle von os.walk, daher ist es etwas einfacher. Allerdings erfolgt die Rekursion nicht einfach durch die Verwendung der Attribute common_dirs und subdirs, da wir in diesem Fall implizit die Standard-"flache" Implementierung des Dateivergleichs verwenden würden, was wahrscheinlich nicht das ist, was Sie wollen. In der folgenden Implementierung vergleichen wir immer nur den Inhalt von Dateien mit demselben Namen.

import filecmp
import os.path

def are_dir_trees_equal(dir1, dir2):
    """
    Vergleicht zwei Verzeichnisse rekursiv. Dateien in jedem Verzeichnis werden als gleich angesehen, wenn ihre Namen und Inhalte übereinstimmen.

    @param dir1: Pfad zum ersten Verzeichnis
    @param dir2: Pfad zum zweiten Verzeichnis

    @return: True, wenn die Verzeichnisbäume identisch sind und beim Zugriff auf die Verzeichnisse oder Dateien keine Fehler aufgetreten sind, andernfalls False.
    """

    dirs_cmp = filecmp.dircmp(dir1, dir2)
    if len(dirs_cmp.left_only)>0 or len(dirs_cmp.right_only)>0 or \
        len(dirs_cmp.funny_files)>0:
        return False
    (_, mismatch, errors) =  filecmp.cmpfiles(
        dir1, dir2, dirs_cmp.common_files, shallow=False)
    if len(mismatch)>0 or len(errors)>0:
        return False
    for common_dir in dirs_cmp.common_dirs:
        new_dir1 = os.path.join(dir1, common_dir)
        new_dir2 = os.path.join(dir2, common_dir)
        if not are_dir_trees_equal(new_dir1, new_dir2):
            return False
    return True

0 Stimmen

Der zweite filecmp.cmpfiles mit shallow=False ist nicht notwendig. Man kann dirs_cmp.diff_files aus dem ersten dircmp erhalten. Ein häufiges Missverständnis (das wir auch hatten!) ist, dass dir_cmp nur oberflächlich ist und keine Dateiinhalte vergleicht! Es stellt sich heraus, dass dies nicht stimmt! Die Bedeutung von shallow=True besteht nur darin, Zeit zu sparen, und betrachtet zwei Dateien mit unterschiedlichen letzten Änderungszeiten nicht tatsächlich als unterschiedlich. Wenn die letzte Änderungszeit unterschiedlich ist, wird zum Lesen der Inhalte jeder Datei übergegangen und verglichen. Wenn die Inhalte identisch sind, ist es eine Übereinstimmung, auch wenn die letzte Änderung unterschiedlich ist.

0 Stimmen

import os sollte ausreichen

23voto

filecmp.dircmp ist der richtige Weg. Aber es vergleicht nicht den Inhalt von Dateien, die sich mit dem gleichen Pfad in zwei verglichenen Verzeichnissen befinden. Stattdessen betrachtet filecmp.dircmp nur Dateiattribute. Da dircmp eine Klasse ist, können Sie dies mit einer dircmp-Unterklasse beheben und ihre phase3-Funktion überschreiben, die Dateien vergleicht, um sicherzustellen, dass der Inhalt verglichen wird, anstatt nur os.stat-Attribute zu vergleichen.

import filecmp

class dircmp(filecmp.dircmp):
    """
    Vergleicht den Inhalt von dir1 und dir2. Im Gegensatz zu filecmp.dircmp vergleicht diese Unterklasse den Inhalt von Dateien mit dem gleichen Pfad.
    """
    def phase3(self):
        """
        Finde Unterschiede zwischen gemeinsamen Dateien.
        Stelle sicher, dass wir den Inhalt mit shallow=False vergleichen.
        """
        fcomp = filecmp.cmpfiles(self.left, self.right, self.common_files,
                                 shallow=False)
        self.same_files, self.diff_files, self.funny_files = fcomp

Dann können Sie dies verwenden, um einen Boolean zurückzugeben:

import os.path

def is_same(dir1, dir2):
    """
    Vergleicht den Inhalt von zwei Verzeichnisbaumstrukturen.
    Gibt False zurück, wenn sie sich unterscheiden, True, wenn sie identisch sind.
    """
    compared = dircmp(dir1, dir2)
    if (compared.left_only or compared.right_only or compared.diff_files 
        or compared.funny_files):
        return False
    for subdir in compared.common_dirs:
        if not is_same(os.path.join(dir1, subdir), os.path.join(dir2, subdir)):
            return False
    return True

Falls Sie diesen Code-Auszug wiederverwenden möchten, wird er hiermit in die Public Domain oder die Creative Commons CC0 nach Ihrer Wahl gewidmet (zusätzlich zur Standardlizenz CC-BY-SA von SO).

1 Stimmen

FWIW, dieser Code-Schnipsel wird in die Public Domain freigegeben oder unter CC0 1.0 nach Ihrer Wahl.

1 Stimmen

Hinweis: Um dies rekursiv zu ermöglichen, müssen Sie auch: (1) das methodmap-Attribut überschreiben (2) phase4 überschreiben, damit subdir Instanzen Ihrer Klasse erzeugt.

0 Stimmen

@Vidar bist du sicher? Hast du ein Beispiel, das nicht funktionieren würde? Die Rekursion wird hier in is_same behandelt.

8voto

Guillaume Vincent Punkte 11278

Hier eine einfache Lösung mit einer rekursiven Funktion:

import filecmp

def same_folders(dcmp):
    if dcmp.diff_files or dcmp.left_only or dcmp.right_only:
        return False
    for sub_dcmp in dcmp.subdirs.values():
        if not same_folders(sub_dcmp):
            return False
    return True

same_folders(filecmp.dircmp('/tmp/archive1', '/tmp/archive2'))

1 Stimmen

Nett ... aber bist du dir bewusst, dass diff_files nicht die ganze Geschichte erzählt (nur Dateien mit demselben Pfad, die jedoch geändert wurden)? Du musst auch nach dircmp.left_only und dircmp.right_only suchen, das sind jeweils neue und gelöschte Dateien in der Quelle.

6voto

asthasr Punkte 8754

Die Methode report_full_closure() ist rekursiv:

comparison = filecmp.dircmp('/Verzeichnis1', '/Verzeichnis2')
comparison.report_full_closure()

Bearbeiten: Nach der Bearbeitung des OP würde ich sagen, dass es am besten ist, einfach die anderen Funktionen in filecmp zu verwenden. Ich denke, dass os.walk unnötig ist; es ist besser, einfach durch die von common_dirs usw. erzeugten Listen zu rekursieren, obwohl dies in einigen Fällen (große Verzeichnisbäume) ein Fehler durch die maximale Rekursionstiefe riskieren könnte, wenn es schlecht implementiert ist.

3voto

Raullen Chai Punkte 195

Eine weitere Lösung besteht darin, das Layout von dir1 und dir2 zu vergleichen und den Inhalt der Dateien zu ignorieren

Siehe Gist hier: https://gist.github.com/4164344

Bearbeiten: Hier ist der Code, falls der Gist aus irgendeinem Grund verloren geht:

import os

def compare_dir_layout(dir1, dir2):
    def _compare_dir_layout(dir1, dir2):
        for (dirpath, dirnames, filenames) in os.walk(dir1):
            for filename in filenames:
                relative_path = dirpath.replace(dir1, "")
                if os.path.exists( dir2 + relative_path + '\\' +  filename) == False:
                    print relative_path, filename
        return

    print 'Dateien in "' + dir1 + '" aber nicht in "' + dir2 +'"'
    _compare_dir_layout(dir1, dir2)
    print 'Dateien in "' + dir2 + '" aber nicht in "' + dir1 +'"'
    _compare_dir_layout(dir2, dir1)

compare_dir_layout('xxx', 'yyy')

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X