2015-02-09 67 views
7

我試圖編寫一個Python函數,遞歸地刪除所有空目錄。這意味着如果目錄「a」僅包含「b」,則應刪除「b」,則應刪除「a」(因爲它現在不包含任何內容)。如果一個目錄包含任何內容,它將被跳過。圖說:爲什麼python的os.walk()不能反映刪除目錄?

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

鑑於此,三個目錄「B」,「A」和「富」應該被刪除,因爲「foo」和「B」現在是空的,而「一」將成爲空刪除「b」後。

我試圖通過os.walkshutil.rmtree來做到這一點。不幸的是,我的代碼只是刪除了第一層目錄,而不是在這個過程中新清空的目錄。

我正在使用topdown=false參數os.walkdocumentationos.walk表示「如果topdown爲False,則在其所有子目錄的三元組(即自下而上生成的目錄)之後生成目錄的三元組」。這不是我所看到的。

這裏是我的代碼:

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 

如果我上面描述的目錄結構,這裏就是我得到:

./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'] 

需要注意的是,即使我已經刪除「B」,「一個「不會被刪除,認爲它仍然包含」b「。我感到困惑的是,os.walk的文檔說它生成「./a」的三元組,在之後生成「b」的三元組。我的輸出表明否則。類似的故事「./c」。它表明它仍然具有「foo」,即使我已經將它刪除了。

我在做什麼錯? (我使用Python 2.6.6。)

+0

我不希望操作系統。步行更新每個迭代的'for'循環 – jcfollower 2015-02-09 20:36:56

+0

我想這是關鍵。文檔中的「之前」和「之後」是指'os.walk()'輸出結果數組中的順序,而不是通過'for'循環連續迭代的時間順序。調用者在'topdown = True'模式下可以修改'dirnames'參數,這讓我認爲迭代可能會受到影響。 – seanahern 2015-02-09 20:55:42

回答

2

jcfollower的答案是關於您遇到的問題的原因絕對正確的:文件系統總是讀自上而下的,即使結果是從os.walk以自下而上的方式產生。這意味着您執行的文件系統修改將不會反映在後面的結果中。

解決這個問題是維護一組被刪除的目錄,這樣就可以篩選出來的子目錄父母的名單:

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 

有三個新的生產線。首先,在頂部創建一個空的removed設置爲包含已刪除的目錄。第二個將dirs列表替換爲不包含已刪除集合中的任何子目錄的新列表,因爲它們在上一步中被刪除。最後一行在刪除時將當前目錄添加到集合中。

+0

這是一個巧妙的把戲!非常聰明。它承認'os.walk()'會給你提供可能被刪除失效的信息,並明確修改它返回的信息。 – seanahern 2015-02-10 16:38:28

9

documentation有這個...

無論自上而下的價值,子目錄的列表 元組的目錄和它之前檢索生成子目錄 。

+0

這是迄今爲止最好的答案。它說''topdown = False'主要是'os.walk()'輸出中的數據排序問題,而不是基礎文件系統探索的時間順序。 – seanahern 2015-02-09 21:15:48