Ausschlüsse in os.walk ausschließen

147

Ich schreibe ein Skript, das in einen Verzeichnisbaum absteigt (mit os.walk ()) und dann jede Datei besucht, die einer bestimmten Dateierweiterung entspricht. Da jedoch einige der Verzeichnisbäume, für die mein Tool verwendet wird, auch Unterverzeichnisse enthalten, die wiederum eine Menge nutzloser (für den Zweck dieses Skripts) Materials enthalten, habe ich mir vorgenommen, eine Option hinzuzufügen, die der Benutzer angeben kann Eine Liste der Verzeichnisse, die vom Durchlauf ausgeschlossen werden sollen.

Dies ist mit os.walk () einfach genug. Schließlich liegt es an mir, zu entscheiden, ob ich die jeweiligen Dateien / Verzeichnisse von os.walk () tatsächlich besuchen oder einfach überspringen möchte. Das Problem ist, dass wenn ich zum Beispiel einen Verzeichnisbaum wie diesen habe:

root--
     |
     --- dirA
     |
     --- dirB
     |
     --- uselessStuff --
                       |
                       --- moreJunk
                       |
                       --- yetMoreJunk

und ich möchte nutzloses Zeug und alle seine Kinder ausschließen, os.walk () wird immer noch in alle (möglicherweise Tausende von) Unterverzeichnissen von nutzlosem Zeug absteigen , was natürlich die Dinge sehr verlangsamt. In einer idealen Welt könnte ich os.walk () anweisen, sich nicht einmal mehr die Mühe zu machen, Kinder von nutzlosem Zeug zur Welt zu bringen , aber meines Wissens gibt es keine Möglichkeit, dies zu tun (gibt es?).

Hat jemand eine Idee? Vielleicht gibt es eine Drittanbieter-Bibliothek, die so etwas bietet?

antred
quelle

Antworten:

242

Durch Ändern dirs an Ort und Stelle werden die (nachfolgenden) Dateien und Verzeichnisse entfernt, die besucht werden von os.walk:

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    dirs[:] = [d for d in dirs if d not in exclude]

Von der Hilfe (os.walk):

Wenn topdown wahr ist, kann der Aufrufer die Liste der Verzeichnisnamen direkt ändern (z. B. über Del- oder Slice-Zuweisung), und walk wird nur in die Unterverzeichnisse zurückgeführt, deren Namen in Verzeichnisnamen verbleiben. Dies kann verwendet werden, um die Suche zu beschneiden ...

unutbu
quelle
31
Warum dirs[:] =?
Ben
55
@ Ben: dirs[:] = valueändert dirs an Ort und Stelle . Es ändert den Inhalt der Liste, dirsohne den Container zu ändern. Wie bereits help(os.walk)erwähnt, ist dies erforderlich, wenn Sie die Art und Weise beeinflussen möchten, wie os.walkdie Unterverzeichnisse durchlaufen werden. ( dirs = valuedirsdirs
Ordnet
6
Sie können auch verwenden filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclearPeon
2
@ p014k: Sie könnten Ihre eigene Generatorfunktion schreiben, die aufruft os.walkund nachgibt, root, dirs, filesnachdem Sie ausgeschlossen haben .git(oder was auch immer Sie wünschen) dirs.
Unutbu
3
@unutbu Nur um Sie wissen zu lassen, dass diese Optimierung in einem Fall die Durchlaufzeit von mehr als 100 Sekunden auf etwa 2 Sekunden reduziert hat. Das nenne ich eine lohnende Optimierung. : D
antred
8

... eine alternative Form von @ unutbu ist eine ausgezeichnete Antwort , die ein wenig direkt gegeben mehr liest, dass die Absicht ist ausschließen Verzeichnisse, auf Kosten von O (n ** 2) vs O (n) Zeit.

( list(dirs)Für die korrekte Ausführung ist eine Kopie der Verzeichnisliste mit erforderlich.)

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]
Dmitri
quelle
5
Wenn Sie auf Kosten des Speichers direkter sein möchten, sollten Sie besser schreiben dirs[:] = set(dirs) - exclude. Zumindest ist es immer noch \ $ O (n) \ $ und Sie bauen nicht nur ein Verständnis für seine Nebenwirkungen auf ...
301_Moved_Permanently
2
Dies ist meiner Meinung nach nicht wirklich schlecht, aber auch kein idiomatisches Python.
Torsten Bronger