os.walk ohne in die folgenden Verzeichnisse zu graben


102

Wie beschränke ich mich darauf os.walk, nur Dateien in dem von mir bereitgestellten Verzeichnis zurückzugeben?

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList

2
Ein weiterer Fall, in dem die Vielzahl möglicher Ansätze und alle damit verbundenen Einschränkungen darauf hindeuten, dass diese Funktionalität zur Python-Standardbibliothek hinzugefügt werden sollte.
antred

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Falls Sie nur die Dateinamen benötigen, verwenden Sie f.namestatt f.path. Dies ist die schnellste Lösung und viel schneller als jede anderewalk oder listdirsiehe stackoverflow.com/a/40347279/2441026 .
user136036

Antworten:


105

Verwenden Sie die walklevelFunktion.

import os

def walklevel(some_dir, level=1):
    some_dir = some_dir.rstrip(os.path.sep)
    assert os.path.isdir(some_dir)
    num_sep = some_dir.count(os.path.sep)
    for root, dirs, files in os.walk(some_dir):
        yield root, dirs, files
        num_sep_this = root.count(os.path.sep)
        if num_sep + level <= num_sep_this:
            del dirs[:]

Es funktioniert genauso wie os.walk, aber Sie können ihm einen levelParameter übergeben, der angibt, wie tief die Rekursion gehen wird.


3
Geht diese Funktion tatsächlich durch die gesamte Struktur und löscht dann die Einträge unter einem bestimmten Punkt? Oder ist etwas klügeres los? Ich bin mir nicht mal sicher, wie ich das mit Code überprüfen soll. - Python Anfänger
Mathtick

1
@mathtick: Wenn ein Verzeichnis auf oder unter der gewünschten Ebene gefunden wird, werden alle Unterverzeichnisse aus der Liste der Unterverzeichnisse entfernt, die als nächstes gesucht werden sollen. Sie werden also nicht "gelaufen".
Nosklo

2
Ich habe dies nur + 1 getan, weil ich Probleme damit hatte, Verzeichnisse zu "löschen". Ich hatte es versucht dirs = []und dirs = Noneaber die funktionierten nicht. map(dirs.remove, dirs)funktioniert, aber mit einigen unerwünschten '[Keine]' Nachrichten gedruckt. Warum also del dirs[:]speziell?
Zach Young

4
Beachten Sie, dass dies bei Verwendung topdown=Falsein os.walk nicht funktioniert . Siehe den 4. Absatz in den Dokumenten :Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated.
21:58

3
@ZacharyYoung dirs = []und dirs = Nonefunktioniert nicht, weil sie nur ein neues, nicht verwandtes Objekt erstellen und dem Namen zuweisen dirs. Das ursprüngliche Listenobjekt muss direkt geändert werden, nicht der Name dirs.
Nosklo

205

Verwenden Sie os.walk nicht.

Beispiel:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item

1
@ 576i: Dies unterscheidet nicht zwischen Dateien und Verzeichnissen

3
@Alexandr os.path.isfileund os.path.isdirlässt Sie unterscheiden. Ich verstehe es nicht, da os.path.isfilees seit '08 im Beispielcode steht und Ihr Kommentar von '16 stammt. Dies ist eindeutig die bessere Antwort, da Sie nicht beabsichtigen, ein Verzeichnis zu durchsuchen, sondern es aufzulisten.
Daniel F

@ DanielF, was ich hier gemeint habe ist, dass Sie alle Elemente durchlaufen müssen, während walkSie sofort die separaten Listen von Verzeichnissen und Dateien erhalten.

Ah, ok. Eigentlich scheint Alex 'Antwort besser zu sein (mit .next()) und es ist viel näher an Ihrer Idee.
Daniel F

Python 3.5 verfügt über eine os.scandirFunktion, die eine komplexere Interaktion zwischen Datei oder Verzeichnisobjekt ermöglicht. Siehe meine Antwort unten
Ascripter

48

Ich denke, die Lösung ist eigentlich sehr einfach.

verwenden

break

Um nur die erste Iteration der for-Schleife durchzuführen, muss es einen eleganteren Weg geben.

for root, dirs, files in os.walk(dir_name):
    for f in files:
        ...
        ...
    break
...

Wenn Sie os.walk zum ersten Mal aufrufen, werden Tulpen für das aktuelle Verzeichnis zurückgegeben und in der nächsten Schleife der Inhalt des nächsten Verzeichnisses.

Nehmen Sie das Original-Skript und fügen Sie einfach eine Pause hinzu .

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
        break
    return outputList

9
Dies sollte die akzeptierte Antwort gewesen sein. Durch einfaches Hinzufügen einer "Unterbrechung" nach der Schleife "für f in Dateien" wird die Rekursivität gestoppt. Möglicherweise möchten Sie auch sicherstellen, dass topdown = True ist.
Alecz

23

Der Vorschlag listdirist gut. Die direkte Antwort auf Ihre Frage in Python 2 lautet root, dirs, files = os.walk(dir_name).next().

Die entsprechende Python 3-Syntax lautet root, dirs, files = next(os.walk(dir_name))


1
Oh, ich habe alle möglichen lustigen Fehler von diesem bekommen. ValueError: zu viele Werte zum
Entpacken

1
Nett! Fühlt sich aber wie ein Hack an. Zum Beispiel, wenn Sie einen Motor einschalten, ihn aber nur eine Umdrehung machen lassen und dann den Schlüssel ziehen, um ihn sterben zu lassen.
Daniel F

Stolperte darüber; root, dirs, files = os.walk(dir_name).next()gibt mirAttributeError: 'generator' object has no attribute 'next'
Evan

3
@Evan, wahrscheinlich, weil dies aus dem Jahr 2008 stammt und die Python 2-Syntax verwendet. In Python 3 können Sie schreiben root, dirs, files = next(os.walk(dir_name))und dann entsprechen die Variablen root, dirs, filesnur den Variablen des Generators auf der dir_nameEbene.
CervEd

13

Sie können verwenden os.listdir(), um eine Liste von Namen (sowohl für Dateien als auch für Verzeichnisse) in einem bestimmten Verzeichnis zurückzugeben. Wenn Sie zwischen Dateien und Verzeichnissen unterscheiden müssen, rufen Sie os.stat()jeden Namen auf.


9

Wenn Sie komplexere Anforderungen als nur das oberste Verzeichnis haben (z. B. VCS-Verzeichnisse ignorieren usw.), können Sie auch die Liste der Verzeichnisse ändern, um zu verhindern, dass os.walk diese erneut durchläuft.

dh:

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        dirs[:] = [d for d in dirs if is_good(d)]
        for f in files:
            do_stuff()

Hinweis - Achten Sie darauf, die Liste zu mutieren, anstatt sie nur erneut zu binden. Offensichtlich weiß os.walk nichts über die externe Rückbindung.


6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep

4

Die gleiche Idee mit listdir, aber kürzer:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]

3

Ich hatte das Gefühl, meine 2 Pence hineinzuwerfen.

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]

2

In Python 3 konnte ich Folgendes tun:

import os
dir = "/path/to/files/"

#List all files immediately under this folder:
print ( next( os.walk(dir) )[2] )

#List all folders immediately under this folder:
print ( next( os.walk(dir) )[1] )

Dies funktioniert auch für Python 2. Wie bekomme ich die zweite Ebene?

2

Seit Python 3.5 können Sie os.scandiranstelle von verwenden os.listdir. Anstelle von Zeichenfolgen erhalten Sie DirEntryim Gegenzug einen Iterator von Objekten. Aus den Dokumenten:

Die Verwendung von scandir()anstelle von listdir()kann die Leistung von Code, der auch Dateityp- oder Dateiattributinformationen benötigt, erheblich steigern, da DirEntryObjekte diese Informationen verfügbar machen, wenn das Betriebssystem sie beim Scannen eines Verzeichnisses bereitstellt. Alle DirEntryMethoden können einen Systemaufruf durchführen, aber is_dir()und is_file()in der Regel nur einen Systemaufruf für symbolische Links erfordern; DirEntry.stat()erfordert unter Unix immer einen Systemaufruf, unter Windows jedoch nur einen für symbolische Links.

Sie können auf den Namen des Objekts zugreifen DirEntry.name, über den dann die Ausgabe von entsprichtos.listdir


1
Nicht nur „kann“ Sie verwenden, Sie sollten verwenden scandir(), da es eine ist viel schneller als listdir(). Siehe Benchmarks hier: stackoverflow.com/a/40347279/2441026 .
user136036

1

Sie können auch Folgendes tun:

for path, subdirs, files in os.walk(dir_name):
    for name in files:
        if path == ".": #this will filter the files in the current directory
             #code here

2
Wird diese Schleife nicht unnötig alle Unterverzeichnisse und Dateien durchlaufen?
Pieter

0

So habe ich es gelöst

if recursive:
    items = os.walk(target_directory)
else:
    items = [next(os.walk(target_directory))]

...

0

Bei der Verwendung von listdir gibt es einen Haken. Der os.path.isdir (Bezeichner) muss ein absoluter Pfad sein. So wählen Sie Unterverzeichnisse aus:

for dirname in os.listdir(rootdir):
  if os.path.isdir(os.path.join(rootdir, dirname)):
     print("I got a subdirectory: %s" % dirname)

Die Alternative besteht darin, in das Verzeichnis zu wechseln, um die Tests ohne os.path.join () durchzuführen.


0

Sie können dieses Snippet verwenden

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1

0

Erstellen Sie eine Liste mit Ausschlüssen, überspringen Sie mit fnmatch die Verzeichnisstruktur und führen Sie den Vorgang aus

excludes= ['a\*\b', 'c\d\e']
for root, directories, files in os.walk('Start_Folder'):
    if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
        for root, directories, files in os.walk(nf_root):
            ....
            do the process
            ....

das gleiche wie für 'enthält':

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):

0

Warum nicht einfach ein rangeund os.walkkombiniert mit dem zip? Ist nicht die beste Lösung, würde aber auch funktionieren.

Zum Beispiel so:

# your part before
for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
    # logic stuff
# your later part

Funktioniert für mich auf Python 3.

Auch: A breakist übrigens auch einfacher. (Schauen Sie sich die Antwort von @Pieter an)


0

Eine kleine Änderung an Alex 'Antwort, aber mit __next__():

print(next(os.walk('d:/'))[2]) oder print(os.walk('d:/').__next__()[2])

mit dem [2]Wesen der filein root, dirs, filein anderen Antworten erwähnt


0

Änderungen am Stammordner für jedes Verzeichnis, das os.walk findet. Ich löse diese Überprüfung, ob root == Verzeichnis

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        if root == dir_name: #This only meet parent folder
            for f in files:
                if os.path.splitext(f)[1] in whitelist:
                    outputList.append(os.path.join(root, f))
                else:
                    self._email_to_("ignore")
    return outputList

0
import os

def listFiles(self, dir_name):
    names = []
    for root, directory, files in os.walk(dir_name):
        if root == dir_name:
            for name in files:
                names.append(name)
    return names

1
Hallo Rich, willkommen bei Stack Overflow! Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte kurzfristige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
kenny_k
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.