【问题标题】:How to catch a File Not Found message within a Windows batch file如何在 Windows 批处理文件中捕获 File Not Found 消息
【发布时间】:2020-11-11 13:59:36
【问题描述】:

我在一个名为 upload_filenames.txt 的文件中有一个包含大约 600 个文件名的列表,我想找出它们在树中的位置,该树在 9K 子目录中有大约 .7M 文件。

这个(来自this question)完成了这项工作:

for /F "usebackq delims=" %%i in (upload_filenames.txt) do (
    for /F "delims=" %%b in ('dir /B /S /A:-D "%%i"') do (
        echo %%~nxb;"%%~fb" >> exists.txt
    )
)

现在,在同一个循环中,我还想用找到的所有文件 not 填充第二个文件。 (我可以从两个列表中手动获取,但我更喜欢自动方式。)

到目前为止,我了解到 FOR 循环和 if exist 在成功时返回 errorlevel 0,但只有“未找到文件”而没有错误级别。所以我不能用那些。那么有没有办法批量做到这一点呢?

旁白:我不在乎效率。上面的脚本大约需要 10 个小时才能完成。就这样吧 - 暂时。

Windows 10 或 Server 2008

【问题讨论】:

  • 对于找到的文件,单行,@(For /F Delims^= %%G In ('Dir /B /S /A:-D ^| "%SystemRoot%\System32\findstr.exe" /E /I /L /G:"upload_filenames.txt"') Do @Echo %%~nxG,%%G) 1> "exists.txt"
  • @Compo 不错!还没遇到findstring

标签: batch-file windows-10 windows-server-2008


【解决方案1】:

一次性重定向整个循环比逐行编写要快得多。重复的dir 也需要很长时间。只需执行一次dir(进入文件)并使用该结果。 findstr 非常有效,所以我想在for 循环中进行后处理而不是if 会更快。

@echo off
setlocal
dir /b /s /a-d * > "files.txt"
(for /F "usebackq delims=" %%i in ("upload_filenames.txt") do (
  for /f "delims=" %%b in ('findstr /iec:"\\%%i" "files.txt" ^|^| echo ~') do (
    echo %%i;%%b
  )
)) > "result.txt"
findstr /ev ";~" "result.txt" > "existing.txt"
findstr /e ";~" "result.txt" > "missing.txt"
rem del "result.txt"

key-line 是findstr /iec:"\\%%i" files.txt || echo ~,当它以文件名结尾时将输出该行,如果找不到文件,findstr 将不输出任何内容。在这种情况下(|| 充当“如果上一个命令失败”(source)),echo 命令将执行并输出~(更改为您想要的任何字符串,但它必须是 任何东西,因为for 循环会跳过空行)

【讨论】:

  • || 充当“如果上一个命令失败则”(还有&&“如果上一个命令成功则”)。转义特殊字符(如 |in 子句中是必要的(因为它在辅助进程中执行)。Setlocal 在这里并不是必需的(我的习惯是使用它不留下任何变量脚本结束后还活着)
  • Source || 技巧。
  • 也许您应该将findstr 命令行更改为findstr /I /E /C:"\\%%i" "files.txt" 以避免文件名中的空格问题(请记住findstr 用空格分隔多个搜索字符串,除非提供/C)并强制文字搜索。此外,名称以元字符开头的文件(如.[]-)可能会失败,因为\ 被用于转义(即使是文字搜索!),所以@987654350 @是一个被维护的转义反斜杠......
  • 谢谢你,@aschipfl,这是正确的。我相应地更改了我的代码。
  • 仅供参考,这在 9K 子目录中的上述 .7M 文件上工作并在大约 15 分钟内完成。确实很不错!
【解决方案2】:

如果我正确地得到了你想要的东西,那么这应该可以解决问题。 未经测试!以后只能测试这个:

@echo off
for /F "usebackq delims=" %%a in ("upload_filenames.txt") do (
for /f "delims=" %%i in ('dir /b /s /a:d') do (
    pushd "%%~i"
    dir /b /a-d | findstr /i "%%~a">nul && echo %%~a;"%%~i%%~a">>exists.txt || echo %%~a not found in "%%~i">>not_exist.txt
    popd
  )
)

或者如果您不喜欢使用条件运算符:

@echo off
for /F "usebackq delims=" %%a in ("upload_filenames.txt") do (
for /f "delims=" %%i in ('dir /b /s /a:d') do (
    pushd "%%~i"
    dir /b /a-d | findstr /i "%%~a">nul
    if not errorlevel 1 (
        echo %%~a;"%%~i\%%~a">> exists.txt
      ) else (
        echo %%~a not found in "%%~i">>not_exist.txt
  )
  popd
  )
)

所以思路是一个接一个地取文件名,然后dir递归查找每个目录,pushd到目录,然后对文件做一个dir:

【讨论】:

  • 我明白了,但不幸的是它都不起作用。
  • 我只能在以后测试和修复问题。我现在不在笔记本电脑上。
【解决方案3】:

和其他人一样,我会使用findstr,因为它比使用嵌套的for 循环要快得多。但是,我会扭转局面,让实际存在的文件列表成为搜索字符串,并使用它们来搜索输入列表文件upload_filenames.txt。虽然我无法绕过一个for 循环来从文件路径中导出纯文件名。无论如何,这是代码:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=."                         & rem // (path to target root directory)
set "_MASK=*.*"                       & rem // (file pattern, usually `*.*` for all)
set "_LIST=%~dp0upload_filenames.txt" & rem // (path to file containing name list)
set "_PASS=%~dp0found.txt"            & rem // (path to positive result file)
set "_FAIL=%~dp0missing.txt"          & rem // (path to negative result file)
set "_FULL=%~dpn0_all.tmp"            & rem // (path to a temporary file)
set "_NAME=%~dpn0_names.tmp"          & rem // (path to another temporary file)

rem // Put list of full paths of all files in the target directory tree to a file:
dir /S /B /A:-D "%_ROOT%\%_MASK%" > "%_FULL%"
rem // Reduce list of paths by maintaining the only pure file names:
> "%_NAME%" (
    for /F "usebackq delims= eol=|" %%L in ("%_FULL%") do (
        echo(%%~nxL
    )
)
rem /* Let `findstr` twice do the search, using the file names from the target
rem    directory tree as search strings against the original list file: */
findstr /I /X    /L /G:"%_NAME%" "%_LIST%" > "%_PASS%"   
findstr /I /X /V /L /G:"%_NAME%" "%_LIST%" > "%_FAIL%"
rem // Clean up temporary files:
del "%_FULL%" "%_NAME%"

endlocal
exit /B

这是一种稍微不同的方法,它返回现有文件的完整路径,当文件名可能在给定目录树中多次出现时,这变得很重要:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=."                         & rem // (path to target root directory)
set "_MASK=*.*"                       & rem // (file pattern, usually `*.*` for all)
set "_LIST=%~dp0upload_filenames.txt" & rem // (path to file containing name list)
set "_PASS=%~dp0found.txt"            & rem // (path to positive result file)
set "_FAIL=%~dp0missing.txt"          & rem // (path to negative result file)
set "_AUGM=%_LIST%.tmp"               & rem // (path to temporary list file)
set "_FULL=%~dpn0_all.tmp"            & rem // (path to a temporary file)
set "_NAME=%~dpn0_names.tmp"          & rem // (path to another temporary file)

rem // Create augmented copy of list file with each line preceded by `\\`:
> "%_AUGM%" (
    for /F "usebackq delims= eol=|" %%L in ("%_LIST%") do (
        echo(\\%%~L
    )
)
rem // Put list of full paths of all files in the target directory tree to a file:
dir /S /B /A:-D "%_ROOT%\%_MASK%" > "%_FULL%"
rem // Reduce list of paths by maintaining the only pure file names:
> "%_NAME%" (
    for /F "usebackq delims= eol=|" %%L in ("%_FULL%") do (
        echo(%%~nxL
    )
)
rem /* Let `findstr` do a search, using the augmented list file against the file
rem    containing the list of full paths in order to eventually get full paths,
rem    which is particularly important if file names are not unique in the tree: */
findstr /I /E    /L /G:"%_AUGM%" "%_FULL%" > "%_PASS%"
rem /* Let `findstr` do another search, using the file names from the target
rem    directory tree as search strings against the original list file this time: */
findstr /I /X /V /L /G:"%_NAME%" "%_LIST%" > "%_FAIL%"
rem // Clean up temporary files:
del "%_AUGM%" "%_FULL%" "%_NAME%"

endlocal
exit /B

N. B.: 幸运的是,nasty flaw of findstr with multiple literal search strings 在这里不适用,因为我们正在执行不区分大小写的搜索。无意识转义也没有问题,因为纯文件名不能包含\,也就是findstr的转义字符。

【讨论】:

  • 非常详细,但由于这种方法,命中文件的前面没有filename.ext;,需要使审查更容易。它在大约 40 分钟内完成。
猜你喜欢
  • 2020-01-30
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
  • 2014-02-24
  • 1970-01-01
相关资源
最近更新 更多