【问题标题】:How to recursively delete all folders of a folder tree unless they contain a file with certain file extension?如何递归删除文件夹树的所有文件夹,除非它们包含具有特定文件扩展名的文件?
【发布时间】:2019-01-31 08:41:18
【问题描述】:

如何遍历目录树并删除所有目录,除非它们包含具有特定文件扩展名的文件?

我尝试 Robocopy 认为文件夹是空的。但是所有文件夹都有隐藏文件。因此,我需要一些东西,例如将目录中没有 .pdf 的目录中的每个文件夹删除并删除。

【问题讨论】:

    标签: windows batch-file recursion cmd directory


    【解决方案1】:

    任务是删除所有不包含 PDF 文件且不包含包含 PDF 文件的子目录/子文件夹的目录/文件夹。让我们看一个例子来更好地理解目录/文件夹删除任务。

    目录C:\Temp包含以下子文件夹和文件:

    • 文件夹 1
      • 子文件夹 A
        • 文件 1A.txt
      • 子文件夹 B
        • 文件 1B.txt
      • 子文件夹 C
        • 文件 1C.pdf
      • 文件 1.cmd
    • 文件夹 2
      • 子文件夹 A
      • 子文件夹 B
        • 文件 2B.pdf
      • 子文件夹 C
        • 文件 2C.pdf
      • 文件 2.jpg
    • 文件夹 3
      • 子文件夹 A
        • 文件 3A.log
      • 子文件夹 B
      • 文件 3.doc
    • 最后一个文件夹和结束
      • 子文件夹 A
        • 最后一个文件 A.xls
      • 子文件夹 B
      • 子文件夹 C
        • 最后一个文件 C.pdf

    文件夹的格式为粗体。 隐藏文件夹的格式为粗体和斜体。隐藏的文件是斜体格式。

    运行批处理文件后想要的文件夹和文件应该是:

    • 文件夹 1
      • 子文件夹 C
        • 文件 1C.pdf
      • 文件 1.cmd
    • 文件夹 2
      • 子文件夹 B
        • 文件 2B.pdf
      • 子文件夹 C
        • 文件 2C.pdf
      • 文件 2.jpg
    • 最后一个文件夹和结束
      • 子文件夹 C
        • 最后一个文件 C.pdf

    这个结果可以通过执行以下批处理文件来实现:

    @echo off
    goto MainCode
    
    :ProcessFolder
    for /F "delims=" %%I in ('dir "%~1" /AD /B 2^>nul') do call :ProcessFolder "%~1\%%I"
    if exist "%~1\*.pdf" goto :EOF
    for /F "delims=" %%I in ('dir "%~1" /AD /B 2^>nul') do goto :EOF
    if /I "%~1\" == "%BatchFilePath%" goto :EOF
    rd /Q /S "%~1"
    goto :EOF
    
    :MainCode
    setlocal EnableExtensions DisableDelayedExpansion
    set "BatchFilePath=%~dp0"
    if exist "C:\Temp\" cd /D "C:\Temp" & call :ProcessFolder "C:\Temp"
    endlocal
    

    在目录树上递归地做某事需要有一个递归调用自身的子例程/函数/过程。在上面的批处理文件中,这是ProcessFolder

    请阅读Where does GOTO :EOF return to? 上的答案。命令goto :EOF 在此处用于退出子程序ProcessFolder,并且仅在启用命令扩展的情况下按需要工作。此处使用的 FORCALL 也需要启用命令扩展。

    批处理文件的主要代码首先明确启用此批处理文件所需的命令扩展,并禁用延迟的环境变量扩展以处理名称中带有感叹号的正确文件夹。这是 Windows 上的默认环境,但最好在此处显式设置此环境,因为批处理文件包含命令 RD 和选项 /Q /S,这对错误执行可能非常有害环境或目录。

    子例程ProcessFolder 不像往常一样位于批处理文件的末尾,上面有goto :EOF,以避免在完成整个任务后意外掉入子例程的命令行。出于安全原因,子程序位于批处理文件的中间。因此,如果用户尝试在不支持命令扩展的情况下在 Windows 95/98 上执行批处理文件,则不会发生任何坏事,因为第一个 goto MainCode 按预期成功执行,但是 SETLOCAL 命令行调用子例程最后也是 ENDLOCAL 失败,所以这个为 Windows 设计的批处理文件没有删除任何目录,cmd.exe 作为 Windows 命令处理器而不是command.com

    主代码还将当前目录设置为要处理的目录。所以C:\Temp 本身永远不会被这段代码删除,因为 Windows 会阻止删除一个目录,该目录是任何正在运行的进程的当前目录,或者包含由正在运行的进程打开的文件,并设置了文件访问权限以防止其他进程删除被进程打开的文件。

    接下来调用带有参数C:\Temp 的子程序ProcessFolder 以递归方式处理此文件夹。

    最后恢复初始环境,如果该目录仍然存在,则还包括启动批处理文件时的初始当前目录。

    for /D 命令通常用于对目录的所有子目录执行某些操作。但这在这里是不可能的,因为 FOR 总是忽略具有隐藏属性集的目录和文件。出于这个原因,有必要使用命令 DIR 来获取当前目录中所有子目录的列表,包括具有隐藏属性集的目录。

    命令行dir "%~1" /AD /B 2>nulFOR 在后台以cmd.exe /C 启动的单独命令进程中执行。这就是为什么这个批处理文件很慢的原因之一。另一个原因是一次又一次地调用子程序,导致cmd.exe内部一次又一次地保存和恢复环境。

    请阅读有关Using Command Redirection Operators 的Microsoft 文章以了解2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令 FOR 之前处理此命令行时被解释为文字字符> 在后台启动的单独命令进程中执行嵌入的dir 命令行。

    对于目录中的每个子目录,子例程ProcessFolder 调用自身。如果目录不再包含一个子目录,则子例程中的第一个 FOR 循环将被保留。

    然后子程序检查当前目录是否至少有一个*.pdf 文件。即使目录仅包含隐藏的 PDF 文件,此处使用的 IF 条件也为真。在这种情况下,子程序不做任何事情就退出了,因为该目录肯定包含一个PDF文件,因此必须根据文件夹删除任务的要求进行保存。

    下一步检查当前目录是否仍然包含至少一个子目录,因为在这种情况下,当前目录也必须保留,因为它的子目录之一至少包含一个 PDF 文件。

    最后子程序检查当前目录是否偶然包含批处理文件,因为该目录也必须保留以完成批处理文件的处理。

    否则当前目录将被删除,所有文件不包含 PDF 文件和子目录,也不是当前正在运行的批处理文件,只要 Windows 不会因为缺少权限或共享访问冲突而阻止删除该目录.

    请注意,批处理文件不会删除目录中未删除的其他文件,这也可以在示例中看到。

    要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

    • call /?
    • cd /?
    • dir /?
    • echo /?
    • endlocal /?
    • for /?
    • goto /?
    • if /?
    • rd /?
    • setlocal /?

    【讨论】:

      猜你喜欢
      • 2012-11-29
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 2012-12-15
      • 2012-11-19
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多