除非它们包含具有某些文件扩展名的文件,否则如何递归删除文件夹树的所有文件夹?

时间:2018-08-24 21:03:12

标签: windows batch-file recursion cmd directory

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

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

1 个答案:

答案 0 :(得分: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

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

运行批处理文件后所需的文件夹和文件应为:

  • 文件夹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,并且仅在启用了命令扩展名的情况下才能使用该命令。此处使用的 FOR CALL 也需要启用的命令扩展名。

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

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

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

接下来称为带有参数ProcessFolder的子例程C:\Temp,以递归方式处理此文件夹。

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

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

FOR 在后台以dir "%~1" /AD /B 2>nul开头的单独命令过程中执行命令行cmd.exe /C。这是此批处理文件非常慢的原因之一。另一个原因是一次又一次地调用该子例程,这会导致cmd.exe内部的一次又一次地保存和恢复环境。

请阅读有关Using Command Redirection Operators的Microsoft文章,以获取2>nul的解释。当Windows命令解释器在执行命令之前处理此命令行时,重定向操作符>必须在 FOR 命令行上使用脱字符号^进行转义,才能被解释为文字字符。 FOR ,它将在后台启动的单独命令进程中执行嵌入式dir命令行。

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

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

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

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

否则,将删除当前目录,其中所有文件均不包含PDF文件,不包含子目录,并且不删除当前正在运行的批处理文件,只要Windows由于权限不足或共享访问冲突而无法阻止删除该目录

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

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

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