文本文件中 variables/filenames 中包含空格的批处理文件

Batch file with spaces in variables/filenames in text file

我完全没有受过编码方面的教育,所以您必须原谅任何糟糕的术语!

我从网上为批处理文件挑选了几段代码(其中一些来自本网站),并且我试图重写其中的一些代码以满足我的需要。

它创建一个新目录,然后在另一个目录及其所有子目录中查找 .txt 列表中的文件名,然后将这些文件复制到在开始时创建的目录中。列表中未找到的任何文件都将写入另一个文本文件以跟踪丢失的任何内容。

密码是:

md 00FoundImages

set LIST=%cd%\imagelist.txt
set FILESPATH=%cd%\testimages
set DEST=%cd%[=11=]FoundImages

for /f "delims=" %%x in (%LIST%) do (forfiles /p %FILESPATH% /s /m %%x* /c "cmd /c move /y @path %DEST%\@file" 2>>errors.txt)

这在大多数情况下都能正常工作,除了以下情况:

  1. 文本文件中的文件名包含 space
  2. 批处理文件所在目录的路径包含space(或其他特殊字符)

我试过像这样用引号引起来:

set FILELIST="%cd%\imagelist.txt"
set FILESPATH="%cd%\testimages"
set DESTPATH="%cd%[=12=]FoundImages"

一旦在目录名称中找到 space,forfiles 命令就会失败。

我试过用引号括起变量,如下所示:

for /f "delims=" %%x in ("%LIST%") do (forfiles /p "%FILESPATH%" /s /m %%x* /c "cmd /c move /y @path "%DEST%\@file"" 2>>errors.txt)

当我从名为 'testing - Copy' 的目录中 运行 批处理文件时,我将错误写入 errors.txt 文件

ERROR: Invalid argument/option - '-'.
Type "FORFILES /?" for usage.

这是正在发生的事情的打印:

E:\ImageAutomationStuff\testing - Copy>md 00FoundImages
A subdirectory or file 00FoundImages already exists.

E:\ImageAutomationStuff\testing - Copy>set LIST=E:\ImageAutomationStuff\testing - Copy\imagelist.txt

E:\ImageAutomationStuff\testing - Copy>set FILESPATH=E:\ImageAutomationStuff\testing - Copy\testimages

E:\ImageAutomationStuff\testing - Copy>set DEST=E:\ImageAutomationStuff\testing - Copy[=15=]FoundImages

E:\ImageAutomationStuff\testing - Copy>pause
Press any key to continue . . .

E:\ImageAutomationStuff\testing - Copy>for /F "delims=" %x in ("E:\ImageAutomationStuff\testing - Copy\imagelist.txt") do (forfiles /p "E:\ImageAutomationStuff\testing - Copy\testimages" /s /m %x* /c "cmd /c move /y @path "E:\ImageAutomationStuff\testing - Copy[=15=]FoundImages\@file""  2>>errors.txt )

E:\ImageAutomationStuff\testing - Copy>(forfiles /p "E:\ImageAutomationStuff\testing - Copy\testimages" /s /m E:\ImageAutomationStuff\testing - Copy\imagelist.txt* /c "cmd /c move /y @path "E:\ImageAutomationStuff\testing - Copy[=15=]FoundImages\@file""  2>>errors.txt )

E:\ImageAutomationStuff\testing - Copy>pause
Press any key to continue . . .

根据我所做的研究,我可以看到这种性质的问题被问了很多 - 很抱歉增加了一堆,但由于我的知识有限,恐怕我根本无法解决这个问题拥有!

for /f "delims=" %%x in ("%LIST%") do (forfiles /p "%FILESPATH%" /s /m %%x* /c "cmd /c move /y @path \"%DEST%\@file\"" 2>>errors.txt)

命令 forfiles 运行的引号内的引号必须用反斜杠转义。但不是其他引号,如起始文件夹。

虽然我不明白为什么要将 FORFILES 循环嵌套在另一个 (FOR /F) 循环中,下一个代码片段应该可以解决问题:

for /f "usebackq delims=" %%x in ("%LIST%") do (
  forfiles /p "%FILESPATH%" /s /m "%%~nxx" /c "cmd /c ECHO move /y @path \"%DEST%\"\@file" 2>>errors.txt
)

注:

  • "%FILESPATH%""%%~nxx" 中的双引号;
  • \"%DEST%\";
  • 中转义双引号
  • 普通 @path@file 因为那些 FORFILES 命令变量已经被双引号;
  • 也许您需要重新制定 /m "%%~nxx" 文件掩码(我使用 %%~nxx file name and extension 因为我不知道 %LIST% 文件内容并假设有一个完全限定文件名的列表);
  • 重要move命令仅显示用于调试目的;删除前面的 ECHO 以使其运行,刚调试完。

为避免路径和文件名中出现空格问题,请使用引号。但是当涉及到 forfiles 时你需要小心,因为所有与路径和文件名相关的 @ 变量都被扩展为已经包含在 "" 中的值,因此它可以很容易地碰巧有双引号的值。

我会试一试:

md "%cd%[=10=]FoundImages"

set "LIST=%cd%\imagelist.txt"
set "FILESPATH=%cd%\testimages"
set "DEST=%cd%[=10=]FoundImages"

for /f "usebackq delims=" %%x in ("%LIST%") do (
    forfiles /p "%FILESPATH%" /s /m "%%x*" /c "cmd /c if @isdir==FALSE cd /d 0x22%DEST%0x22 & move /y @path @file" 2>>errors.txt
)

我做了什么:

  • 改进了 set 的语法,使整个赋值表达式包含在 "" 中;这避免了一些特殊字符的麻烦,但是引号是 not 变量值的一部分;
  • 引用了 %LIST%,所以我不得不将 usebackq 选项添加到 for /f,因为它会将变量 %LIST% 的值视为文字字符串而不是否则为文件;
  • /p之后的路径以及forfiles/m之后的模式用括号括起来;这里非常重要的是 /p 之后的路径是 而不是 \ 终止,因为这将被 forfiles 解释为转义字符转义结束引号 "如果您不能确定 %FILESPATH% 满足该标准,请改写 /p "%FILESPATH%\." 编辑:最后一个陈述是错误的!
  • @path 变量扩展为已引用的字符串,因此一切正常;
  • 棘手的部分是原始代码中的目标文件 %DEST%\@file,因为 @file 扩展为已经引用的字符串,因此结果将类似于 \...[=32=]FoundImages\"FoundImage 1.png",例如;因此引用整个表达式,如 0x22%DEST%\@file0x221 将导致 "\...[=34=]FoundImages\"FoundImage 1.png"",因此文件名 FoundImage 1.png 在命令解释器中未被引用;使用 0x22%DEST%0x22\@file1 会导致 "\...[=37=]FoundImages"\"FoundImage 1.png",这不会损害很多命令,但可能会影响一些,而且它根本不美观;要解决这个问题,只需先通过 cd /d 0x22%DEST%0x221 切换到目录 %DEST%,因此单独说明,然后仅对 @file 使用 move命令行;
  • 最后,forfiles returns 匹配文件 目录,所以为了确保只处理文件,我实现了一个 if查询;

1)... 0x22表达式代表forfiles /C命令表达式中的一个"字符,用十六进制表示其字符代码;我更喜欢这种指定引号的方式,因为另一个选项 \" 可能会影响命令行解释器,因为它不支持反斜杠转义,因此它会识别引号,这可能会无意中改变其行为。