2015-02-09 7 views
7

Ich versuche, eine Python-Funktion zu schreiben, die alle leeren Verzeichnisse rekursiv löscht. Das bedeutet, wenn das Verzeichnis "a" nur "b" enthält, sollte "b" gelöscht werden, dann sollte "a" gelöscht werden (da es jetzt nichts enthält). Wenn ein Verzeichnis irgendetwas enthält, wird es übersprungen. Illustriert:Warum spiegelt os.walk() von Python das Löschen von Verzeichnissen nicht wider?

top/a/b/ 
top/c/d.txt 
top/c/foo/ 

Vor diesem Hintergrund sind die drei Verzeichnisse „b“, „a“ und „foo“ gelöscht werden soll, als „foo“ und „b“ sind jetzt leer, und „a“ wird leer werden nach dem Löschen von "b".

Ich versuche dies über os.walk und shutil.rmtree zu tun. Leider löscht mein Code nur die erste Ebene von Verzeichnissen, aber nicht die neu geleerten Verzeichnisse.

Ich verwende den topdown=false Parameter von os.walk. Die documentation für os.walk sagt, dass "Wenn topdown False ist, wird das Triple für ein Verzeichnis nach den Tripeln für alle seine Unterverzeichnisse generiert (Verzeichnisse werden von unten nach oben generiert)." Das sehe ich nicht.

Hier ist mein Code:

for root, dirs, files in os.walk(".", topdown=False): 
    contents = dirs+files 
    print root,"contains:",contents 
    if len(contents) == 0: 
    print 'Removing "%s"'%root 
    shutil.rmtree(root) 
    else: 
    print 'Not removing "%s". It has:'%root,contents 

Wenn ich die Verzeichnisstruktur oben beschrieben haben, ist hier, was ich bekommen:

./c/foo contains: [] 
Removing "./c/foo" 
./c contains: ['foo', 'd.txt'] 
Not removing "./c". It has: ['foo', 'd.txt'] 
./a/b contains: [] 
Removing "./a/b" 
./a contains: ['b'] 
Not removing "./a". It has: ['b'] 
. contains: ['c', 'a'] 
Not removing ".". It has: ['c', 'a'] 

Beachten Sie, dass, obwohl ich "b" entfernt habe " a "wird nicht entfernt und denkt, dass es immer noch" b "enthält. Was mich verwirrt, ist, dass die Dokumentation für os.walk besagt, dass es das Tripel für "./a" nach generiert, das das Tripel für "b" generiert. Meine Ausgabe schlägt anders vor. Ähnliche Geschichte für "./c". Es zeigt, dass es immer noch "foo" hat, obwohl ich es direkt aus dem Gate gelöscht hatte.

Was mache ich falsch? (Ich verwende Python 2.6.6.)

+0

Ich würde nicht erwarten, os.Gehen Sie, um auf jeder Wiederholung der 'for' Schleife aktualisiert zu werden – jcfollower

+0

Ich denke, das ist der Schlüssel. Das "Vorher" und das "Nachher" in der Dokumentation beziehen sich auf die Reihenfolge in dem resultierenden Array, das von "os.walk()" ausgegeben wird, und nicht auf eine zeitliche Reihenfolge aufeinanderfolgender Iterationen durch die for-Schleife. Die Tatsache, dass der Aufrufer im 'topdown = True'-Modus das' dirnames'-Argument ändern kann, ließ mich glauben, dass die Iteration beeinflusst werden könnte. – seanahern

Antwort

2

jcfollower Antwort ist absolut richtig über die Ursache des Problems Sie stoßen: Das Dateisystem immer top-down gelesen wird, auch wenn die Ergebnisse von os.walk in einer Bottom-up Art und Weise nachgegeben werden. Dies bedeutet, dass die von Ihnen durchgeführten Dateisystemänderungen in den späteren Ergebnissen nicht berücksichtigt werden.

Eine Lösung für dieses Problem ist es, ein Satz der gelöschten Verzeichnisse zu erhalten, so dass man sie aus ihrem Eltern Liste der Verzeichnisse filtern:

removed = set()            # first new line 
for root, dirs, files in os.walk(".", topdown=False): 
     dirs = [dir for dir in dirs if os.path.join(root, dir) not in removed] # second 
     contents = dirs+files 
     print root,"contains:",contents 
     if len(contents) == 0: 
      print 'Removing "%s"'%root 
      shutil.rmtree(root) 
      removed.add(root)         # third new line 
     else: 
      print 'Not removing "%s". It has:'%root,contents 

Es gibt drei neue Linien. Die erste, ganz oben, erstellt ein leeres removed, das die entfernten Verzeichnisse enthält. Die zweite ersetzt die Liste dirs durch eine neue Liste, die keine Unterverzeichnisse enthält, die sich in der entfernten Gruppe befinden, da sie in einem vorherigen Schritt gelöscht wurden. Die letzte neue Zeile fügt dem Satz das aktuelle Verzeichnis hinzu, wenn es entfernt wurde.

+0

Das ist ein netter Trick! Sehr schlau. Es bestätigt, dass "os.walk()" Ihnen Informationen geben wird, die möglicherweise durch die Löschungen ungültig gemacht wurden, und ändert explizit, was zurückgegeben wird. – seanahern

9

Die documentation dies hat ...

Ganz gleich der Wert der Top-Down, die Liste der Unterverzeichnisse wird für das Verzeichnis vor den Tupeln abgerufen und seine Unterverzeichnisse werden generiert.

+0

Dies ist die beste Antwort bisher. Es besagt, dass "topdown = False" in erster Linie eine Frage der Datenreihenfolge in der Ausgabe von "os.walk()" ist, nicht die zeitliche Reihenfolge der zugrundeliegenden Dateisystem-Exploration. – seanahern

Verwandte Themen