【问题标题】:Add prefix to filenames using batch files [duplicate]使用批处理文件为文件名添加前缀[重复]
【发布时间】:2019-12-04 03:21:15
【问题描述】:

我可以使用以下方法为一系列文本文件添加前缀:

:: rename files
for %%a in (*.txt) do (
ren "%%a" "Seekret file %%a"
:: ECHO %%a Seekret file %%a
)

哪个会转

 a.txt
 b.txt
 c.txt

进入

 Seekret file a.txt
 Seekret file b.txt
 Seekret file c.txt

但是,上面的代码似乎用前缀重命名了第一个文件两次。我最终得到了

Seekret 文件 Seekret 文件 a.txt

我不知道为什么。有什么想法吗?

【问题讨论】:

  • 不要在带括号的代码块中使用像::这样的无效标签作为cmets,因为它们可能会导致奇怪的意外结果;改用rem...

标签: windows batch-file for-loop cmd rename


【解决方案1】:

是的,这可能会发生,尤其是在 FAT32 和 exFAT 驱动器上,因为这些文件系统不会将通配符模式匹配的目录条目列表返回给按字母顺序调用的可执行文件。 for 一个接一个地处理与*.txt 匹配的目录条目,命令ren 导致更改目录条目,即在迭代时修改文件名列表。

解决方法是:

for /F "eol=| delims=" %%I in ('dir *.txt /A-D /B 2^>nul') do ren "%%I" "Seekret file %%I"

FOR 在这种情况下在后台 %ComSpec% /c 中运行,命令行在 ' 之间指定,这意味着 Windows 安装到目录 C:\Windows:

C:\Windows\System32\cmd.exe /C dir *.txt /A-D /B 2>nul

于是又在后台启动了一个命令进程,它执行 DIR

  • 在当前目录中搜索
  • 仅用于文件,因为选项 /A-D(属性不是目录)
  • 包括设置了隐藏属性的文件(使用/A-D-H 排除隐藏文件)
  • 匹配通配符模式*.txt
  • 并且由于选项 /B 仅以裸格式输出文件名。

通过将其重定向到设备 NUL强>.

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

不带路径的文件名由DIR输出,用于处理后台命令进程的STDOUTFOR 分别由执行批处理文件的命令进程捕获此输出。

启动的命令进程自行终止后,FOR 处理捕获的文件名列表。由于这个原因,在循环迭代期间对目录所做的所有更改都不再重要。文件名列表不再改变。

需要eol=| delims= 选项才能将完整的文件名依次分配给循环变量I,即使以; 开头或包含空格字符也是如此。 eol=| 将默认的行尾字符 ; 重新定义为一个文件名不能包含的竖线。 delims= 定义了一个空的分隔符列表,以禁用普通空格和水平制表符上的默认行拆分行为。

注意: :: 是无效标签,不是评论。命令块内的标签是不允许的,并且通常会导致执行命令块时出现未定义的行为。使用命令 REM(备注)进行评论。

更好的是:

for /F "eol=| delims=" %%I in ('dir *.txt /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /I /L /V /C:"Seekret file "') do ren "%%I" "Seekret file %%I"

FINDSTR在这里用于从DIR输出的文件名列表中输出并重定向到FINDSTRSTDIN > 所有文件名

  • 因为/V(反转结果)
  • 开始 因为选项/B
  • 不区分大小写,因为选项 /I
  • 由于选项 /L 被解释为 literally(与 /C: 冗余)
  • 字符串Seekret file 

选项/C: 需要指定包含两个空格的搜索字符串,因为仅使用"Seekret file" 将导致在行首搜索Seekretfile 时不区分大小写。在仅使用"..." 指定的搜索字符串中,FINDSTR 将每个空格解释为类似于 Perl 正则表达式字符串中的| 的 OR 表达式。

使用/C: 指定的搜索字符串被隐式解释为文字字符串,但使用/R(而不是/L)可以将此字符串解释为正则表达式字符串,在该字符串上解释空格作为空间而不是作为 OR 表达式。可以使用多次/C:指定多个搜索字符串。

我对使用 FINDSTR 的建议:始终使用 /L/R 以明确 FINDSTR 以及命令行的每个读者如何FINDSTR 应该解释用"..."/C:"..." 指定的搜索字符串。

【讨论】:

    【解决方案2】:

    我想我也会把我的帽子扔进去,因为我真的不喜欢循环通过 dir 输出,而且目前没有其他人负责这个脚本已经运行:

    @echo off
    
    set "dir=C:\Your\Root\Directory"
    set "pfx=Seekret file "
    
    setlocal enabledelayedexpansion
    for /r "%dir%" %%A in (*.txt) do (
        set "txt=%%~nA"
        if not "!txt:~0,13!"=="%pfx%" ren "%%A" "%pfx%%%~nxA"
    )
    
    pause
    

    for /r 将递归遍历所有 .txt 文件,将每个文件设置为参数 %%A(每次迭代),将变量 txt 设置为参数 %%A 简化为它的名称(%%~nA),然后它将文本文件的前 13 个字符与您的示例前缀(包含空格时为 13 个字符长:Seekret file)进行比较 - 如果它们匹配,则循环不执行任何操作;如果它们不匹配,则循环将重命名 %%A 以在开头包含前缀。如果您不希望它是递归的,则可以改用for %%A in ("%dir%"\*.txt) do (。除此之外,您只需更改!txt:~0,13!,具体取决于您的前缀是什么或您要检查的文件名中有多少个字母。您也不必设置目录和前缀变量,我只是更喜欢这样做,因为它使块看起来更干净 - 与脚本中出现该值的每个位置相比,返回并更改一个值更容易。

    参考:for /rrenvariable substrings

    【讨论】:

    • 很好的解决方案,有一个小限制:由于启用了延迟的环境变量扩展,因此文件名中带有一个或多个感叹号的 *.txt 文件无法正确处理。但是文件名中的! 非常少见,因此该代码几乎适用于所有用例。如果文本文件不是通过此批处理代码重命名的,而是由在编辑文件名时未准确键入 Seekret file 的用户手动重命名,则不区分大小写的字符串比较可能也是一个好主意。
    【解决方案3】:

    使用

    for /f "delims=" %%a in ('dir /b /a-d *.txt') do (
    

    发生的情况是您使用的版本将重命名的文件视为新文件。 dir 版本构建了一个文件名列表,然后 在每一行上执行 for,因此该列表已经构建并且是静态的,cmd 不会尝试对移动的文件进行操作目标。

    另外 - 在代码块(带括号的指令序列)中使用 rem,而不是 ::,因为这种形式的注释实际上是一个损坏的标签,code block 中不允许使用标签。

    【讨论】:

    • 更进一步:如果您使用的是常量前缀并且这不是您第一次运行前缀添加循环 - 您需要检查是否存在现有文件的前缀,否则您将遇到与以前相同的问题。这可以通过将您的参数与变量子字符串进行比较并仅重命名不匹配的那些来完成(举一个例子)。 ss64.com/nt/syntax-substring.html
    猜你喜欢
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 2015-12-24
    • 2015-03-16
    • 2014-02-26
    • 2023-02-23
    • 2020-10-11
    • 2017-09-16
    相关资源
    最近更新 更多