除了列表文件中指定的文件外,如何分别删除目录中的所有文件和文件夹以及整个目录树?

How delete all files and folders in a directory respectively an entire directory tree except files specified in a list file?

我想知道如何使用批处理文件解决以下问题。

删除 G:\AD\mods 中的所有文件和文件夹,除了我要保留的所有文件和文件夹的列表。

有人对批处理文件解决方案有什么建议吗?

我在批处理文件中尝试了以下 Windows 命令行:

for %%i in (*.*) do if not "%%i"=="doc1.txt" if not "%%i"=="doc2.txt" del /q "%%i"

这有效,但我只能从与批处理文件相同的文件夹中删除文件。

然后我读了这个post:Delete all files in folder except file in list using batch

它建议类似:

except 3.txt del temp /Q

这也适用于同一文件夹,但不适用于目录树不同级别的文件和文件夹。

编辑:

我有一个包含大约 1000 个子文件夹的主文件夹。这些文件夹中还有子文件夹。多个文件夹中可能存在相同的文件名。所以我需要为我想保留的每个文件指定确切的文件路径。

要保留的文件列表文件中的名称示例:

AD\mods\folder1\doc1.txt
AD\mods\folder1\subfolder1\doc1.txt
AD\mods\folder2\doc1.txt

批处理文件应删除所有其他文件。

PowerShell 解决方案也可以。

让我们假设列表文件 G:\AD\mods\Exclusion List.txt 仅包含 file/folder 个没有路径的名称,例如:

Cleanup.cmd
Exclusion List.txt
Folder to keep
File not to delete.txt

批处理文件G:\AD\mods\Cleanup.cmd可以使用以下命令行删除批处理文件目录中的所有文件和文件夹,排除列表文件中的文件和文件夹除外:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FolderPath=%~dp0"
for /F "eol=| delims=" %%I in ('dir "%FolderPath%" /AD /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /G:"%FolderPath%Exclusion List.txt"') do rd /Q /S "%FolderPath%%%I"
for /F "eol=| delims=" %%I in ('dir "%FolderPath%" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /G:"%FolderPath%Exclusion List.txt"') do del /A /F "%FolderPath%%%I"
endlocal

第一个FOR命令行在一个单独的命令进程中运行,在后台启动命令DIR输出目录中的所有文件夹名称使用 FINDSTR 过滤的批处理文件从文件夹名称列表中排除排除列表文件中列出的所有文件夹名称。结果列表由 FOR 处理,它运行命令 RD 来删除这些文件夹。

第二个FOR命令行在一个单独的命令进程中运行,在后台启动命令DIR输出目录中的所有文件名使用 FINDSTR 过滤的批处理文件从文件名列表中排除排除列表文件中列出的所有文件名。结果列表由 FOR 处理,它运行命令 DEL 来删除这些文件。

FOR 选项 eol=| 用于避免忽略以分号开头的 file/folder 名称,因为没有 file/folder 名称可以包含垂直酒吧。 FOR 选项 delims= 定义了一个空的字符串定界符列表,导致关闭 for /F 的默认行为,即使用常规方法将捕获的行拆分为子字符串(标记) space 和水平制表符作为字符串分隔符,仅将第一个 space/tab 分隔的字符串分配给指定的循环变量 I。该选项对于更正处理 file/folder 具有一个或多个 space 的名称是必需的。


任务要求更改为删除整个目录树中的所有文件,排除列表文件中列出的带路径(无盘符和冒号)的文件除外。

因此让我们假设文件 G:\AD\mods\Exclusion List.txt 包含以下行。

G:\AD\mods\Cleanup.cmd
G:\AD\mods\Exclusion List.txt
AD\mods\folder1\doc1.txt
AD\mods\folder1\subfolder1\doc1.txt
AD\mods\folder2\doc1.txt

批处理文件G:\AD\mods\Cleanup.cmd可以使用以下命令行首先删除包含批处理文件和排除列表文件的目录的整个目录树中的所有文件,排除中的文件除外列出文件,然后递归地删除所有不再包含至少一个文件的目录。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FolderPath=%~dp0"
if "%FolderPath:~-1%" == "\" set "FolderPath=%FolderPath:~0,-1%"
for /F "delims=" %%I in ('dir "%FolderPath%" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /E /I /L /V /G:"%FolderPath%\Exclusion List.txt"') do del /A /F "%%I"
call :DeleteEmptyFolders "%FolderPath%"
goto EndBatch

:DeleteEmptyFolders
for /F "eol=| delims=" %%I in ('dir %1 /AD /B 2^>nul') do call :DeleteEmptyFolders "%~1\%%I"
for /F "eol=| delims=" %%I in ('dir %1 /A /B 2^>nul') do goto :EOF
rd /Q /S %1
goto :EOF

:EndBatch
endlocal

请注意 FINDSTR 选项 /X 在第一个 FOR 命令行中被选项 /E 过滤掉所有文件名,其完全限定名只是 ends 与排除列表文件中的字符串之一而不是匹配整个文件名,因为列表文件包含文件名没有驱动器号和冒号。这可能会导致错误的过滤,具体取决于列表文件中文件完整路径的哪一部分。

那么这个批处理文件在驱动器 G::

的以下目录树上会发生什么
  • AD
    • 备份
      • 文件夹1
        • 子文件夹1
          • ;文件到keep.txt
          • doc1.txt
        • doc1.txt
    • 模组
      • 文件夹1
        • 子文件夹1
          • ;文件到delete.txt
          • doc1.txt
        • doc1.txt
        • 还有一个文件!.txt
      • 文件夹2
        • 清空子文件夹 1
        • 子文件夹 2
          • 另一个空文件夹
          • doc1.txt
        • doc1.txt
        • 文件夹2中的日志文件().log
      • 文件夹3
        • doc1.txt
      • Cleanup.cmd
      • 排除List.txt
      • Readme.txt

G:\AD\mods\Cleanup.cmd的执行结果为:

  • AD
    • 备份
      • 文件夹1
        • 子文件夹1
          • ;文件到keep.txt
          • doc1.txt
        • doc1.txt
    • 模组
      • 文件夹1
        • 子文件夹1
          • doc1.txt
        • doc1.txt
      • 文件夹2
        • doc1.txt
      • Cleanup.cmd
      • 排除List.txt

因此 G:\AD\backups 中的所有内容都将保留,而在 G:\AD\mods 中仅保留其名称列在排除列表文件中的文件夹,批处理脚本将删除所有其他文件和文件夹.

可以使用以下批处理代码创建目录和文件,但 G:\AD\mods 中的 Cleanup.cmdExclusion List.txt 除外:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BasePath=G:"
md "%BasePath%\AD\backups\folder1\subfolder1" 2>nul
md "%BasePath%\AD\mods\folder1\subfolder1" 2>nul
md "%BasePath%\AD\mods\folder2\Empty subfolder 1" 2>nul
md "%BasePath%\AD\mods\folder2\Subfolder 2\Another Empty Folder" 2>nul
md "%BasePath%\AD\mods\folder3" 2>nul
setlocal EnableDelayedExpansion
echo !BasePath!\AD\backups\folder1\subfolder1\;File to keep.txt>"!BasePath!\AD\backups\folder1\subfolder1\;File to keep.txt"
echo !BasePath!\AD\backups\folder1\subfolder1\doc1.txt>"!BasePath!\AD\backups\folder1\subfolder1\doc1.txt"
echo !BasePath!\AD\backups\folder1\doc1.txt>"!BasePath!\AD\backups\folder1\doc1.txt"
(echo !BasePath!\AD\mods\folder1\subfolder1\;File to delete.txt>"!BasePath!\AD\mods\folder1\subfolder1\;File to delete.txt") 2>nul
echo !BasePath!\AD\mods\folder1\subfolder1\doc1.txt>"!BasePath!\AD\mods\folder1\subfolder1\doc1.txt"
echo !BasePath!\AD\mods\folder1\doc1.txt>"!BasePath!\AD\mods\folder1\doc1.txt"
echo !BasePath!\AD\mods\folder1\One More File^^!.txt>"!BasePath!\AD\mods\folder1\One More File^!.txt"
(echo !BasePath!\AD\mods\folder2\Subfolder 2\doc1.txt>"!BasePath!\AD\mods\folder2\Subfolder 2\doc1.txt") 2>nul
echo !BasePath!\AD\mods\folder2\doc1.txt>"!BasePath!\AD\mods\folder2\doc1.txt"
echo !BasePath!\AD\mods\folder2\Log file in folder2().log>"!BasePath!\AD\mods\folder2\Log file in folder2().log"
echo !BasePath!\AD\mods\folder3\doc1.txt>"!BasePath!\AD\mods\folder3\doc1.txt"
echo !BasePath!\AD\mods\Readme.txt>"!BasePath!\AD\mods\Readme.txt"
endlocal
%SystemRoot%\System32\attrib.exe +r "%BasePath%\AD\mods\folder1\subfolder1\;File to delete.txt"
%SystemRoot%\System32\attrib.exe +h "%BasePath%\AD\mods\folder2\Subfolder 2\doc1.txt"
%SystemRoot%\System32\attrib.exe +h "%BasePath%\AD\mods\folder2\Empty subfolder 1"
%SystemRoot%\System32\attrib.exe +r +s +h "%BasePath%\AD\mods\folder3"
endlocal

删除文件夹 G:\AD\mods\folder3 很棘手,因为它具有只读属性集(以及系统和隐藏属性),这会导致在使用命令 Access is denied. 时出现错误消息 rd %1 2>nul 在子例程 DeleteEmptyFolders 中的第一个 FOR 循环之后,否则只会删除空文件夹。出于这个原因,第二个 FOR 循环被使用 运行 再次 DIR 在后台的单独命令进程中。如果目录不为空,因为仍然包含文件或文件夹,则退出子例程。否则文件夹为空,即使设置了只读属性,rd /Q /S %1 也用于删除空文件夹。


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

  • call /? ... 还解释了 %~dp0 ... 参数 0 的驱动器和路径,它是始终以反斜杠结尾的批处理文件路径。
  • del /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • goto /?
  • if /?
  • rd /?
  • setlocal /?

阅读有关 Using command redirection operators 的 Microsoft 文档,了解 2>nul| 的解释。重定向运算符 >| 必须在 FOR 命令行上使用脱字符 ^ 进行转义,以便在 [=366] 时被解释为文字字符=] 命令解释器在执行命令 FOR 之前处理命令行,该命令在后台以 %ComSpec% /c 和作为附加参数附加的嵌入式命令行。

PowerShell 有一条命令供您使用。

Remove-Item -Path 'C:\src\delt\*' -Exclude 't.txt','x.txt'

如果您的文件包含要排除在删除之外的文件列表:

Remove-Item -Path 'delt\*' -Exclude (Get-Content -Path '.\deltexclude.txt')

如果您必须 运行 来自 cmd.exe 或 .bat 文件脚本:

powershell -NoLogo -NoProfile -Command ^
    "Remove-Item -Path 'C:\src\delt\*' -Exclude 't.txt','x.txt'"

阅读有关 Remove-Item 命令的信息:

在 PowerShell 控制台中:

help Remove-Item -Full

在 cmd.exe 控制台中:

powershell "help Remove-Item -Full"