快速递归文件夹删除 - 调用正确的rmdir

时间:2014-08-01 13:27:26

标签: python windows

这个问题并不直接与python绑定,但我需要在windows下的python32下进行工作。

从这个answer开始我假设使用shutil.rmtree() 真的慢(我需要每天删除超过3M个文件,并且需要超过24小时) Windows因此我想使用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?< / p>

是否有替代实用程序(类似于使用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 |
+-------------------+-------------+----------+-----------------+

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

4 个答案:

答案 0 :(得分:1)

调用正确的rmdir

我想到了直接从cmd.exe /C手动调用%SYSTEMROOT%\System32并清除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')

我认为无论系统范围PATH如何,这都适用于任何版本的Windows,但我仍然更喜欢“elegant”

删除所有文件(第一次出错后不会停止)。


使用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以某种方式涉及)。

答案 1 :(得分:1)

  

是的,我发现了这个别名,但是存在同样的问题......如果有人创建了rd.exe(或者已经安装在PATH变量中的任何地方),它就赢了#39工作。   在这种情况下它并没有真正重要,因为重点是无效的rmdir(来自cygwin的那个)被调用,我不想构建一个依赖于没有人的代码将在进程的cwd中创建文件rmdir.exe。

&#34>路径中的任何地方都是&#34;或关注的当前工作目录?如果它是cwd,那么:

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

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

答案 2 :(得分:1)

使用内置插件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 :(得分:0)

根据记录herermdir似乎有一个别名rd。我无法测试它,但你可以尝试一下。

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

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