【问题标题】:How to ignore spaces in variable used in a batch FOR loop如何忽略批处理 FOR 循环中使用的变量中的空格
【发布时间】:2021-08-14 13:50:45
【问题描述】:

我为我孩子的童子军 BSA 部队运行 google drive 帐户,因此每次活动都从所有其他父母那里获得成百上千张照片。我创建了一个简单的小批量,允许我将获得的所有图片放在一个目录中,并将它们重命名为“Some_Event-1.png、Some_Event-2.png”等,并且在大多数情况下,这很有效。但是一些提供的文件名中有空格,这似乎在运行批处理时导致“找不到文件”问题。无论文件名是否有空格,如何让我的 FOR 循环中的重命名命令起作用?我在下面使用的代码:

(注意:我将标志传递给批处理而不是在批处理中使用用户输入。格式为“BATREN.BAT N EXT New_name”,其中 N/%1 是希望它开始的数字EXT/%2 处的编号是我要定位的扩展名,例如 jpg 或 png,而 New_name/%3 是要分配的新名称。)

@ECHO OFF
setlocal enabledelayedexpansion

DIR *.%2 /B>C:\Users\MyUser\AppData\Local\Temp\FileList.txt
Set /A CNT=%1

FOR /F %%i IN (C:\Users\MyUser\AppData\Local\Temp\FileList.txt) DO (
    REN %%i %3-!CNT!.%2
    SET /A CNT+=1
)
EXIT /B

我试图找到与我在这里所问的类似的答案,但似乎没有答案,如果已经有的话,我要么是瞎子要么是白痴,所以我为此道歉。

【问题讨论】:

  • 打开命令提示符窗口,键入for /?,按[ENTER] 键,阅读使用信息,特别注意FOR /F 语法。您需要使用"delims=""tokens=*",以防止默认的空格或制表符分隔您的输出。然后你需要在你的REN 命令中双引号你的文件名字符串,例如REN "P:\ath\To\file.ext" "newname.ext".
  • @Compo 这似乎是this one 的部分副本,但是最后一个要求“我的问题是,如何将带有空格的名称分配给变量?”,所以可能不是一个明确的副本。这个one 也可以匹配,但要引用输出。找到了 dup:this one,抱歉打扰了。

标签: for-loop batch-file whitespace


【解决方案1】:

以下是我如何处理任务的快速示例,无需创建中间文本文件:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "Count=%~1"
For /F Delims^=^ EOL^= %%G In ('Dir "*.%~2" /B /A:-D 2^>NUL') Do (
    SetLocal EnableDelayedExpansion
    Ren "%%G" "%~3-!Count!%%~xG"
    EndLocal
    Set /A Count += 1
)

该示例不验证是否接收到任何参数,或者接收到的参数是否正确。即第一个是整数,第二个是不包含句点的字符串,第三个是不包含任何在 Windows 文件名中无效的字符的字符串。我建议任何强大的解决方案都应该执行该验证,这超出了您的问题范围。此外,没有检查是否已经存在任何预期的文件名,虽然它可能不太可能,但一个强大的解决方案可能应该包含用于确定在这种情况下会发生什么的代码。

但是,根据我的评论,该示例确保没有分隔符,(delims),并用双引号括起文件名字符串,保护空格和其他有问题的字符。此外,它不定义 eof(行首) 字符,因为默认值为 ;,这是文件名开头的有效字符。

您还应该注意,在启用了 8.3 命名的系统上,(默认),文件 glob 示例:*.jpe 不会列出不区分大小写扩展名 .jpe 的文件,它会列出名称以句点结尾的所有项目,后跟字符串jpe,后跟零个或多个非句点字符,其中还包括.jpeg*.tif 也是如此,它也将返回 .tiff 文件。现在这可能不是问题,实际上可能是一个好处,但如果它可能是一个不需要的问题,您可能应该包含一个过滤器以确保只处理与您的输入匹配的扩展,(再次这是在外面您问题的范围)


如果您只是想看看会发生什么,而不实际执行重命名操作,您可以将代码更改为此,它已被修改,以便任何输出都不会省略枚举文件名中可能的 ! 字符, (由于延迟扩展)

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "Count=%~1"
For /F Delims^=^ EOL^= %%G In ('Dir "*.%~2" /B /A:-D 2^>NUL') Do (
    Set "FileName=%%G"
    SetLocal EnableDelayedExpansion
    Echo Ren "!FileName!" "%~3-!Count!%%~xG"
    EndLocal
    Set /A Count += 1
)

您当然也可以将它用于实际的重命名任务,只需从行 7 中删除 Echo

【讨论】:

  • 谢谢!我一定会去看看这个。我确实有参数验证,以及 cmets 和手册页,因为这是我被教导这样做的方式,但为了在此处发布的目的,我认为不需要包含一段 IF 和几个未处理的批次标签。
【解决方案2】:

你的麻烦来自引用和分隔符。

  • FOR 命令指定delims
  • (可选)在REN 命令的右侧添加引号,以防您必须提供“间隔”文件名目标。

编辑:感谢@compo,删除usebackq,因为它是用于命令而不是输出...我老了...

@ECHO OFF
setlocal enabledelayedexpansion

DIR *.%2 /B>C:\Users\MyUser\AppData\Local\Temp\FileList.txt
Set /A CNT=%1

FOR /F "delims=" %%i IN (C:\Users\MyUser\AppData\Local\Temp\FileList.txt) DO (
    REN "%%i" "%3-!CNT!.%2"
    SET /A CNT+=1
)
EXIT /B

解释:delims= 选项告诉FOR 命令获取每个字符,直到遇到换行符。默认情况下,FOR 命令的分隔符是一个空格,这就是为什么当它们有空格时你只会得到部分文件名。

【讨论】:

  • 我不相信以下任何一项是正确的,“usebackq 选项告诉FOR 命令引用其输出,这样您就不必引用%%i使用REN 命令。”,因此您的代码不会保护FileList.txt 中包含的所列文件中的任何空格。
  • 谢谢,这也很有帮助。我最终接受了您的建议并查找了 delims,并且更好地了解了 for /f 现在正在做什么。我最终选择了另一个答案,为此放弃了中间文本文件,但你的建议也帮助了我在批次的其他地方。
猜你喜欢
  • 2016-05-17
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
  • 2016-04-14
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多