当文件名与模式列表不匹配时,批处理脚本删除文件

Posted

技术标签:

【中文标题】当文件名与模式列表不匹配时,批处理脚本删除文件【英文标题】:batch script delete files when file name does not match list of patterns 【发布时间】:2020-10-02 08:30:42 【问题描述】:

我想删除目录中的所有文件,但名称与特定模式匹配的文件除外。该目录下该类文件的典型示例如下:

Bookmarks.xml ---- 删除 DownloadMeta.xml ---- 删除 logfile.log ---- 删除 1745388844.idx ---- 删除 TS1.c.pickle ---- 删除 TS1.prm.bak ---- 删除 !clear.bat ---- 保留 .gitignore ---- 保留 BookFlight.c ---- 保留 CheckItinerary.c ---- 保留 combined_TS1.c ----保留 pre_cci.c ---- 保留 TS1.prm ---- 保留 TS1.usr ---- 保留 vuser_end.c ---- 保留 vuser_init.c ---- 保留 globals.h ---- 保留 ScriptUploadMetadata.xml ---- 保留

我想将带有名称的文件保存在模式列表中:

list_to_ignore = ["!clear.bat", ".gitignore", "*.usr", "default.cfg", 
                    "default.usp", "*.c", "*lobals.h", "*custom_body.h", 
                    "*body_variables.txt", "*loadMetadata.xml", "*.prm" ]

例如:

文件 TS2.c.pickle 与列表中的任何项目都不匹配,因为它以 .pickle 扩展名结尾。它应该被删除。

file somefile.c 匹配“*.c”模式,因为它以 .c 结尾。应该保留。

文件 Metadata.xml 不匹配任何模式,因为它的开头缺少前缀 "load"。它应该被删除。

文件 Globals.h 匹配“lobals.h 模式。应该保留它。

这是我尝试过的:

@echo off
FOR /d %%a in ("./*") DO rd "%%a" /q /s
FOR %%i in (*.*) DO ^
if not "%%i"=="!clear.bat" ^
if not "%%i"==".gitignore" ^
if not "%%i"=="*.usr" ^
if not "%%i"=="default.cfg" ^
if not "%%i"=="default.usp" ^
if not "%%i"=="*.c" ^
if not "%%i"=="*lobals.h" ^
if not "%%i"=="*custom_body.h" ^
if not "%%i"=="*body_variables.txt" ^
if not "%%i"=="*ploadMetadata.xml" ^
if not "%%i"=="*.prm" ^
DEL /s /q "%%i"
pause

Here is a list of all files in a local directory with check marks

希望这是有道理的。如果这不是一个合适的问题,请告诉我。

非常感谢

【问题讨论】:

【参考方案1】:

这个命令的帮助可能会让你走向正确的方向findstr /?

/G:StringsFile从文件中获取搜索字符串(/代表控制台)。

您需要将所有字符串放入一个文件并使用以下语法:

findstr /G:"Filter_File.txt"

/V Print only lines that do NOT contain a match.

/I Case-insensitive search.

所以你可以这样写:

@echo off
Title Batch script delete files when file name does not match list of patterns
Set "Files2Keep=%~dp0Files2Keep.txt"

>"%Files2Keep%" (
    echo    !clear.bat
    echo    .gitignore
    echo    BookFlight.c
    echo    CheckItinerary.c
    echo    combined_TS1.c
    echo    pre_cci.c
    echo    TS1.prm
    echo    TS1.usr
    echo    vuser_end.c
    echo    vuser_init.c
    echo    globals.h
    echo    ScriptUploadMetadata.xml
)

@for /f "delims=" %%a in ('dir /b /a:-d ^|findstr /I /V /G:"%Files2Keep%"') do ( 
    If "%%~nxa" NEQ "%~nx0" (
        IF "%%~dpnxa" NEQ "%Files2Keep%" (
            Echo DEL /s /q "%%a"
        )
    )
)
pause

【讨论】:

这会删除所有内容,包括 Files2Keep.txt 文件 这失败了,因为每个文件都被删除了,什么都没有保留。 @AndrewKozyrev 我编辑了答案以保持文件Files2Keep.txt不被删除! 这是结果: DEL /s /q ".gitignore" ... 它仍然删除所有内容。【参考方案2】:

根据您提供的信息以及.gitignore 中提供的信息,我建议您使用忽略列表,添加一些您自己的信息。

我认为!clear.bat 是您正在寻求帮助的文件,因此我将其作为其内容提供。

@CD /D "%~dp0"
@For /F "EOL=? Delims=" %%G In ('Dir /B /S /A:D') Do @RD /S /Q "%%G"
@(Del /A /F /Q *.bak *.c.pickle *.ci *.dat *.db *.idx *.sdf combined_*.c ^
 Breakpoints.xml CompilerLogMetadata.xml logfile.log mdrv.log mdrv_cmd.txt ^
 options.txt output.txt pre_cci.c ReplaySummaryReport.xml ThumbnailsCache.tmp ^
 UserTasks.xml)

您会注意到,如前所述,虽然您想要保留 .c 文件,但 combined_*.cpre_cci.c 都是在编译期间创建的,而不是必需的。如果您想保留它们,请根据需要将它们从35 行中删除。

【讨论】:

失败:没有删除“some_file.txt” @AndrewKozyrev,您当前的文件列表图像中肯定不存在该文件,而且您之前的图像中肯定也不存在。我特别提到它是基于忽略列表,添加一些你自己的。然后,我使用您链接图像中提供的列表为您添加了一些您自己的列表。除了您提供的信息之外,我不可能知道存在哪些文件!只需将*.txt 添加到列表中! 您的解决方案不是过滤文件,它遍历文件名,硬编码,删除,而不是文件名忽略。但是非常感谢,我很感激! 我知道,这就是我告诉你的!只有绝对的傻瓜才会创建并发布一个永久删除所有文件的脚本。此方法仅删除已知不需要的那些,因为它们在.gitignore 中列出,或者因为您在问题或其随附图片中特别提到了它们! 我用 ! 写了.gitignore 来列出要保留的文件。它不会列出要忽略的文件,而是要保留的文件。【参考方案3】:

这可以通过批处理文件中的以下命令行来完成:

@for /F "eol=| delims=" %%I in ('dir "%~dp0" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /X /V /C:"!clear.bat" /C:".gitignore" /C:"BookFlight.c" /C:"CheckItinerary.c" /C:"combined_TS1.c" /C:"pre_cci.c" /C:"TS1.prm" /C:"TS1.usr" /C:"vuser_end.c" /C:"vuser_init.c" /C:"globals.h" /C:"ScriptUploadMetadata.xml" /C:"%~nx0"') do @del /A /F "%~dp0%%I"

此命令行导致在后台启动另一个命令进程,其中%ComSpec% /c' 之间的命令行作为附加参数附加。如此执行是将 Windows 安装到 C:\Windows 并且批处理文件的完整限定文件名是 C:\Temp\Test.bat:

C:\Windows\System32\cmd.exe /c dir "C:\Temp\" /A-D /B 2>nul | C:\Windows\System32\findstr.exe /I /L /X /V /C:"!clear.bat" /C:".gitignore" /C:"BookFlight.c" /C:"CheckItinerary.c" /C:"combined_TS1.c" /C:"pre_cci.c" /C:"TS1.prm" /C:"TS1.usr" /C:"vuser_end.c" /C:"vuser_init.c" /C:"globals.h" /C:"ScriptUploadMetadata.xml" /C:"Test.bat"

DIR 输出来处理 STDOUT(标准输出)

只是文件名,因为选项 /A-D(属性不是目录) 匹配默认通配符模式*(任何文件名) 在指定目录中找到C:\Temp 由于选项 /B 仅表示文件名和文件扩展名,因此采用裸格式。

在这种情况下,命令 DIR 不太可能输出错误消息,因为找不到与这些条件匹配的目录条目来处理 STDERR(标准错误),因为必须是此目录中的批处理文件。但是2>nul 会重定向此错误消息以处理后台启动的命令进程的 STDERR 以抑制它。

DIR 的输出通过 | 重定向到 FINDSTRSTDIN(标准输入),用于搜索

由于选项/I 不区分大小写 实际上是因为选项/L 由于选项/X而完全匹配的行 使用选项/C: 指定的搜索字符串之一 并输出处理后台命令的 STDOUT 处理反转结果,因为选项 /V 这意味着所有行不完全是任何搜索字符串。

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

FOR 捕获输出以处理后台命令进程的STDOUT,并在启动后逐行处理此输出cmd.exe 在完成命令行执行后自行终止。

FOR 会跳过此处未出现的所有空行。 FOR 接下来将使用字符普通空格和水平制表符作为字符串分隔符将每一行拆分为子字符串。 FOR 将忽略以; 开头的第一个子字符串的行,这是默认的行尾字符。否则,只会将第一个空格/制表符分隔的字符串分配给循环变量 I 以供进一步处理。

这里不需要这种行拆分行为,因为文件名可以包含一个或多个空格,并且可以在 0 个或多个前导空格之后以分号开头。出于这个原因,选项eol=| 用于将竖线定义为文件名不能包含的行尾字符,选项delims= 用于定义字符串分隔符的空列表以关闭将文件名拆分为子字符串.

因此 DIR 输出的每个文件名不是指定为 FINDSTR 的搜索字符串之一的每个文件名都完全分配给循环变量 I FOR 执行命令 DEL 删除文件,因为使用了选项/A,所以该文件独立于隐藏文件,甚至因为选项/F而成为只读文件。

为使用带有正则表达式的 FINDSTR 来过滤掉与其中一种搜索模式匹配的文件名而重写的命令行:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BatchFileName=%~nx0"
set "BatchFileName=%BatchFileName:.=\.%"
for /F "eol=| delims=" %%I in ('dir "%~dp0" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /X /V /C:"!clear\.bat" /C:"\.gitignore" /C:"^.*\.usr" /C:"default\.cfg" /C:"default\.usp" /C:"^.*\.c" /C:"^.*lobals\.h" /C:"^.*custom_body\.h" /C:"^.*body_variables\.txt" /C:"^.*ploadMetadata\.xml" /C:"^.*\.prm" /C:"%BatchFileName%"') do @del /A /F "%~dp0%%I"
endlocal

注意: FINDSTR 选项 /R 代替 /L 用于正则表达式搜索,它需要使用 \ 进行转义才能解释 .作为文字字符,* 被修改为 ^.* 以匹配从第 0 行开始的任何字符或多次。

同样可以通过使用不包含空格字符的批处理文件名来实现:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BatchFileName=%~nx0"
set "BatchFileName=%BatchFileName:.=\.%"
for /F "eol=| delims=" %%I in ('dir "%~dp0" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /X /V "!clear\.bat \.gitignore ^.*\.usr default\.cfg default\.usp ^.*\.c ^.*lobals\.h ^.*custom_body\.h ^.*body_variables\.txt ^.*ploadMetadata\.xml ^.*\.prm %BatchFileName%"') do @del /A /F "%~dp0%%I"
endlocal

FINDSTR 将仅使用 "..." 指定的搜索字符串中的空格解释为 OR 表达式,而使用 /C:"..." 指定的搜索字符串中的空格按字面意思解释为空格字符。

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

call /? ... 解释 %~dp0 ... 参数 0 的驱动器和路径,它始终是 Windows 命令处理器当前执行的批处理文件的完整路径,并始终以反斜杠和 %~nx0 ... 文件名结尾带有批处理文件的扩展名。 del /? dir /? endlocal /? findstr /? for /? set /? setlocal /?

【讨论】:

以上是关于当文件名与模式列表不匹配时,批处理脚本删除文件的主要内容,如果未能解决你的问题,请参考以下文章

返回不需要的文件列表结果的批处理脚本

windows批处理文件不退出

批处理脚本遍历指定文件夹下的文件

批处理脚本下文件与文件夹的操作

批处理删除当前文件夹下所有指定类型文件(包括子目录)

批处理脚本:判断某个文件夹超过5G时,清空该文件夹中2天前的文件?