【问题标题】:Fast recursive folder deletion - calling the right rmdir快速递归文件夹删除 - 调用正确的 rmdir
【发布时间】:2014-09-24 17:21:28
【问题描述】:

这个问题与python没有直接关系,但我需要windows下python32下的工作实现。

从这个answer开始我假设在windows下使用shutil.rmtree()真的很慢(我需要删除超过3M的文件,每天需要超过24小时)所以我想要使用subprocess.call()rmdir,但由于我的%PATH% 系统变量中有cygwin 错误rmdir 被调用,我会得到这个:

>>> args = ['rmdir', r'D:\tmp']
>>> subprocess.call(args)
cygwin warning:
  MS-DOS style path detected: D:\tmp
  Preferred POSIX equivalent is: /cygdrive/d/tmp
  CYGWIN environment variable option "nodosfilewarning" turns off this warning.
  Consult the user's guide for more details about POSIX paths:
    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
rmdir: failed to remove `D:\\tmp': Directory not empty
1

注意:我知道递归删除文件夹需要使用/S /Q

我怎样才能确保调用正确的rmdir(就像在linux下你会使用绝对路径-/bin/rm)最好不使用使用shell=True

是否有替代实用程序(例如使用robocopy /MIR)?


编辑:速度比较

我测试了使用Measure-Command删除1,257,449 个文件,750,251 个文件夹中的 237 GB(255,007,568,228 字节)的不同方法。

+-------------------+-------------+----------+-----------------+
|                   | rmdir /s /q |  shutil  | SHFileOperation |
+-------------------+-------------+----------+-----------------+
| Hours             |           3 |        5 |               6 |
| Minutes           |          26 |       52 |              14 |
| Seconds           |          46 |       13 |              48 |
| TotalMinutes      |         207 |      352 |             375 |
| TotalSeconds      |       12406 |    21134 |           22488 |
| TotalMilliseconds |    12406040 | 21133805 |        22488436 |
+-------------------+-------------+----------+-----------------+

注意:测试是在生产服务器上运行的(因此结果可能会受到影响)

【问题讨论】:

  • 如果你的路径中有 cygwin,你总是可以使用 rm -rf
  • 您遇到的错误似乎与目录不为空有关。如果其中有文件,Windows 将抛出此错误。添加/s 开关使其递归?
  • @FatalError 不幸的是,脚本将在多台机器上运行,我不能依赖它们来安装 cygwin(我可以检查 cygwin 并调用 cygwin 等等,但我想要一个优雅的和简单的解决方案)
  • @Tadgh 在这种情况下我并不真正关心,因为重点是无效的rmdir(来自cygwin)被调用,我不想构建代码这取决于没有人会在进程的cwd 中创建文件rmdir.exe
  • "我认为使用 shutil.rmtree() 真的很慢" - 你是这样认为的,还是你测量过的?

标签: python windows


【解决方案1】:

正确调用rmdir

我想出了一个想法,直接从%SYSTEMROOT%\System32 手动调用cmd.exe /C 并清除env 变量(它似乎有效):

def native_rmdir(path):
    ''' Removes directory recursively using native rmdir command
    '''

    # Get path to cmd
    try:
        cmd_path = native_rmdir._cmd_path
    except AttributeError:
        cmd_path = os.path.join(
            os.environ['SYSTEMROOT'] if 'SYSTEMROOT' in os.environ else r'C:\Windows',
            'System32', 'cmd.exe')
        native_rmdir._cmd_path = cmd_path

    # /C - cmd will terminate after command is carried out
    # /S - recursively, 
    args = [cmd_path, '/C', 'rmdir', '/S', '/Q', path]
    subprocess.check_call(args, env={})


native_rmdir(r'D:\tmp\work with spaces')

我认为这将在任何版本的 Windows 下工作,无论系统范围的 PATH 是什么,但我仍然更喜欢 “优雅”

删除所有可以删除的文件(它不会在第一个错误后停止)。


使用SHFileOperation()

也可以使用SHFileOperation() 来做到这一点[example source]

from win32com.shell import shell, shellcon
shell.SHFileOperation((0, shellcon.FO_DELETE, r'D:\tmp\del', None, shellcon.FOF_NO_UI))

这个将在第一个错误之后停止(当我在我的环境中测试这个解决方案时,这个解决方案往往比shutil.rmtree() 慢,可能是因为以某种方式涉及 UI)。

【讨论】:

  • 这是我建议的解决方案,我认为它足够优雅 - 你需要一个特定的外壳,然后直接调用它。
  • 我发现您的第一个解决方案最好。 rd 是一个内部命令,而不是二进制(外部),所以很自然cmd.exe 是你必须调用它的那个。
  • 这很有帮助!从 shutil.rmtree() 立即减少至少 50% 的时间。需要注意的重要一点是,如果某些要删除的文件正在被另一个进程使用,这会引发subprocess.CalledProcessError
【解决方案2】:

使用内置的os.walkos.removeos.rmdir

需要注意的主要是 Windows 路径。要么使用 / 作为路径分隔符而不是 \,要么使用原始字符串。

但最好在路径名上使用os.path.normpath,例如从命令行获取。

在下面的代码中,topdown=False必不可少的

path = os.path.normpath(path)
for root, dirs, files in os.walk(path, topdown=False):
    for f in files:
        os.remove(os.path.join(root, f))
    for d in dirs:
        os.rmdir(os.path.join(root, d))

可能的速度改进可能是收集列表中的所有文件路径,并将其与multiprocessing.Pool.map() 一起使用以使用多个进程删除文件。之后您可以使用os.removedirs 清理空目录。但这种解决方案也可能会压倒磁盘子系统。

【讨论】:

    【解决方案3】:

    是的,我找到了这个别名,但同样的问题... 在这种情况下它并不重要,因为重点是调用了无效的 rmdir(来自 cygwin 的那个),我不想构建一个依赖于没有人会在 cwd 中创建文件 rmdir.exe 的代码过程。

    那么问题是“路径中的任何位置”还是当前工作目录?如果是 cwd,那么:

     if os.path.exists('rmdir.exe'):
         raise BadPathError("don't run this in an insecure directory")
    

    但潜在的问题是您允许它从某人可以创建rmdir.exe 的目录运行。是的,Windows 权限很弱,但解决起来并不难。

    【讨论】:

      【解决方案4】:

      正如记录的here,似乎rmdir 有一个别名rd。我无法测试它,但你可以试试这个。

      >>> args = ['rd', r'D:\tmp', '/s', '/q']
      >>> subprocess.call(args)
      

      删除隐藏文件或系统文件可能有一些限制 - 我无法测试它。

      【讨论】:

      • 是的,我找到了这个别名,但同样的问题... /跨度>
      • 可能我没有抓住重点,但为什么有人会创建rd.exe?我可以理解 rmdir 对于安装了 cygwin 的用户来说是个问题,但 rd 不是 cygwin 命令。 shutil.rmtree() 在你的情况下真的那么慢吗?
      • 它必须每天删除超过 3M 的文件,批量删除需要超过 24 小时。
      猜你喜欢
      • 2015-08-02
      • 1970-01-01
      • 2011-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-25
      • 2012-10-18
      • 2014-06-21
      相关资源
      最近更新 更多