为什么 python 的 os.walk() 不反映目录删除?

Why does python's os.walk() not reflect directory deletion?

我正在尝试编写一个 Python 函数来递归删除所有空目录。这意味着如果目录 "a" 仅包含 "b",则应删除 "b",然后应删除 "a"(因为它现在什么都不包含)。如果一个目录包含任何东西,它就会被跳过。图说:

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

鉴于此,"b"、"a"和"foo"三个目录应该被删除,因为"foo"和"b"现在是空的,并且"a"删除"b"后会变空。

我正在尝试通过 os.walkshutil.rmtree 执行此操作。不幸的是,我的代码只是删除了第一级目录,而不是在此过程中新清空的目录。

我正在使用 os.walktopdown=false 参数。 os.walkdocumentation 表示 "If topdown is False, the triple for a directory is generated after the triples for all of its subdirectories (directories are generated bottom-up)." 这不是我所看到的。

这是我的代码:

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","a" 并没有被删除,我认为它仍然包含 "b"。我感到困惑的是 os.walk 的文档说它为“./a”生成三元组 为 "b" 生成三元组之后。我的输出表明并非如此。 “./c”的类似故事。它显示它仍然有 "foo",即使我已经删除了它。

我做错了什么? (我正在使用 Python 2.6.6。)

documentation 有这个...

No matter the value of topdown, the list of subdirectories is retrieved before the tuples for the directory and its subdirectories are generated.

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 列表替换为一个新列表,该列表不包含已删除集中的任何子目录,因为它们在上一步中已被删除。最后一行在删除时将当前目录添加到集合中。