【问题标题】:Batch - Ghost File Passing 'If Not Exist' Check批处理 - 通过“如果不存在”检查的 Ghost 文件
【发布时间】:2017-11-19 13:18:41
【问题描述】:

我的代码

我在下面有一些直接的代码:

  1. 检查我的目录中是否存在文件
  2. 运行 for 循环以获取第一个文件名
  3. 根据文件名做事
  4. 删除文件
  5. 检查目录中是否存在任何其他文件(如果存在,重复,如果不存在,继续)

    :MYLOOP
    IF NOT EXIST "%mypath%\*.*" GOTO nofile
    FOR %%F IN ("%mypath%\*.*") DO (
        set filenameWithExt=%%~nxF
        set filename=%%~nF
        set filepath=%%~pF
        )
    do other filename specific tasks
    
    del "%mypath%\%filenameWithExt%"
    
    IF NOT EXIST "%mypath%\*.*" GOTO nofile
    
    GOTO MYLOOP
    
    :nofile
    

我的问题

我反复使用过这段代码,它的工作原理就像一个魅力,但在我最近的使用中,它看起来像是找到了一个“幽灵”文件。当目录中没有没有文件(只有一个存档文件夹)时,上面第 1 步中的if not exist 检查仍然以某种方式通过。结果,for 循环中的set 代码导致:

系统找不到指定的文件。

然后它看起来好像试图删除我的目录,说:

\\mypath*,您确定 (Y/N) 吗?

然后我必须手动终止一个自动批处理。

我的问题

为什么它通过if not exist 检查,而不是跳到:nofile?

我该如何解释这个“幽灵”文件(或者如果它正在检测存档文件夹——我还能如何忽略它)?

【问题讨论】:

  • 那么,在继续之前,您要检查%mypath% 中是否有任何文件吗?
  • 如果您要使用 %mypath% 中的文件,我建议您通过迭代 dir /b "%mypath%\*.*" 的输出来完成。

标签: batch-file file-exists not-exists


【解决方案1】:

Windows 内核以及因此 Windows 命令解释器解释通配符模式 *.* 就像 * 一样,表示任何文件或文件夹。使用通配符模式*.* 并不意味着必须有一个文件(或文件夹)名称中带有一个点。

因此,使用条件IF NOT EXIST "%mypath%\*.*" 与使用IF NOT EXIST "%mypath%\*" 相同。

IF EXIST "%mypath%\*" 通常在批处理文件中用于验证 %mypath% 指定的是文件夹而不是文件,因为此条件检查是否存在文件夹 %mypath%。如果该文件夹存在,则条件为 true,与该文件夹中的文件和文件夹数量无关。

因此,批处理文件顶部的条件不会检查文件夹 %mypath% 中是否至少有 1 个文件,而是检查该文件夹是否根本不存在。

您可以使用以下批处理代码,通过使用子程序来避免使用延迟扩展。

@echo off
for /F "delims=" %%I in ('dir /A-D /B /ON "%mypath%\*" 2^>nul') do call :ProcessFile "%mypath%\%%I"
goto :EOF

:ProcessFile
set "FilenNmeWithExt=%~nx1"
set "FileName=%~n1"
set "FilePath=%~p1"
rem do other filename specific tasks
del "%~1"
goto :EOF

命令FOR执行命令行

dir /A-D /B /ON "%mypath%\*" 2>nul

在后台的单独命令进程中捕获 DIR 的输出以处理 STDOUT

如果目录根本不存在或不包含任何文件,

DIR 将输出错误消息以处理 STDERR。通过使用2>nul 将其重定向到设备NUL 来抑制此错误消息。重定向运算符 > 必须在此处使用插入符号 ^ 进行转义,以便 Windows 命令解释器在解析整个 FOR 命令行时首先将其解释为文字字符,否则将导致语法错误.

选项/A-D 表示DIR 应该输出所有没有设置目录属性的目录条目,即只输出文件,而不是文件夹。 /BDIR 的输出更改为裸格式,这意味着只有文件名而没有任何附加数据。 /ON 导致在 DIR 输出整个列表之前按文件名对列表进行排序。这个选项在这里实际上是不必要的。

FOR 现在处理 DIR 的捕获输出。因此,在 FOR 运行时删除该目录中的文件并不重要。 FOR 将初始列表处理为 DIR 的输出。

对于DIR 输出的每个文件名,都会执行子程序ProcessFile,这就像调用另一个具有该名称的批处理文件一样。传递给子程序的是文件名及其路径。 DIR 仅输出不带路径的文件名,而不使用额外的/S 来获取指定目录及其所有子目录中所有文件名的列表。

FOR 循环后的命令goto :EOF 需要在处理完所有由 DIR 输出的文件名后进入子例程。

如果上面的行是批处理文件的最后一行,则不需要子例程之后的命令goto :EOF。但是,在添加更多命令行(如下面的另一个子例程)的情况下,始终以 goto :EOF 结束子例程通常是一种很好的做法。对于 Windows 命令解释器,以标签开头的子程序在文件中的位置无关紧要。

【讨论】:

  • 哇!感谢@mofi 的详细回复!我已经接受了 Magoo 的回答,但我想快速回复我的感谢。肯定有一些花絮,我会牢记在心。
【解决方案2】:

if exist 测试会在目录中查找 anything

我会重构你的代码:

:MYLOOP
set "found1="
FOR %%F IN ("%mypath%\*.*") DO (
    set filenameWithExt=%%~nxF
    set filename=%%~nF
    set filepath=%%~pF
    set "found1=Y"
    )
if not defined found1 goto nofile

do other filename specific tasks

del "%mypath%\%filenameWithExt%"
GOTO MYLOOP

:nofile

如果for 找不到文件,found1 将保持未定义,因此我们转到:nofile 标签,否则我们有一个文件要处理。删除文件后,回到开头,清除标志并重复...

【讨论】:

  • 还没有实现它,但逻辑对我来说看起来非常合理——显然在这一点上我无法透过树木看到森林。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 2023-03-20
  • 2013-04-27
  • 1970-01-01
相关资源
最近更新 更多