使用正则表达式重命名批处理文件以匹配 4 位数年份

Batch file rename using regex to match 4-digit year

在 windows 批处理文件中,我想重命名文件名中包含 4 位数年份(例如:“1999”)的文件,只需将年份字符串括在括号中即可。示例:

home video 1998.avi
home vid 1987.mov
home_video (2002).avi

会变成

home video (1998).avi
home vid (1987).mov
home_video (2002).avi

请注意,如果它已经包含在括号中,我不希望将它们加倍。

到目前为止,我只能匹配包含年份字符串的文件名,代码如下:

@echo off
REM Match file names with 4-digit year
setlocal enableDelayedExpansion

for /f "tokens=1* delims=" %%A in (
  'dir /B "*"^|findstr "[1-2][0-9][0-9][0-9]" '
) do @echo %%A

pause
REM Now what?

所以我可以输出匹配文件名的列表,但从那里我不知道如何定位 findstr 匹配的分组字符,以便将完整文件名解析为我认为我需要的 3 个块:匹配组之前的子字符串、组本身和组之后的子字符串。

这可以在批处理文件中实现吗?

我将 Total Commander(共享软件)用于 file/folder 重命名任务已超过 20 年,这使得通过其内置的多重重命名工具,只需单击几下即可轻松重命名文件和文件夹结果可以在真正 运行 多重重命名之前查看,甚至支持在完成多重重命名后撤消。好吧,实际上我几乎所有文件管理任务都使用 Total Commander。

但是为这个非常特殊的文件重命名任务开发代码很有趣,它具有命令处理器的所有限制 Windows 因为不是为此类任务设计的。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /R /C:"19[89][0123456789]" /C:"20[012][0123456789]"') do call :RenameFile "%%I"
endlocal
goto :EOF

:RenameFile
set "FileName=%~n1"
setlocal EnableDelayedExpansion
set "Year=1980"

:YearLoop
set "NewName=!FileName:%Year%=(%Year%)!"
if "!NewName!" == "!FileName!" (
    if %Year% == 2029 goto ExitSub
    set /A Year+=1
    goto YearLoop
)
if "!FileName:(%Year%)=!" == "!FileName!" ren "%~1" "!NewName!%~x1"

:ExitSub
endlocal
goto :EOF

FOR 在后台启动的单独命令进程中执行以下命令行 cmd.exe /C:

dir /A-D-H /B 2>nul | C:\Windows\System32\findstr.exe /R /C:"19[89][0123456789]" /C:"20[012][0123456789]"

DIR 使用选项输出当前目录中非隐藏文件的所有名称,仅包含文件名+扩展名,不包含文件路径。当前目录不包含任何非隐藏文件的错误消息输出通过将其从句柄 STDERR 重定向到设备 NUL 来抑制 2>nul.

DIR输出的文件名被|重定向到处理STDIN命令FINDSTR 使用两个正则表达式解释搜索字符串搜索区分大小写的四位数字,范围为 19801999 或范围 20002029。如果四位数字的匹配是更大数字(如 12000 或 19975)的一部分,则不会进行检查。如果四位数字周围已经有圆括号,则不会进行检查。

FINDSTR 也将 ¹²³ 解释为使用 [0-9] 的数字,这就是使用的原因[0123456789] 只真正匹配这 10 个数字字符中的任何一个。请阅读有关 FINDSTR 文章 SS64 - FINDSTR and What are the undocumented features and limitations of the Windows FINDSTR command?

的更多详细信息

FINDSTR 输出包含 19802029 范围内的四位数字的所有文件名,以处理 STDOUT后台命令进程。

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

FOR 捕获这些行并逐行处理它们。选项 eol=(行尾)的默认值是分号,因此 FOR 将忽略所有以分号开头的文件名。出于这个原因,指定了 eol=|,因为在文件名中不能使用竖线,因此所有捕获的文件名都由 FOR.

处理 默认情况下,

FOR 会拆分 spaces/tabs 上的每个文件名,并仅将第一个子字符串(标记)分配给指定的循环变量 I。通过使用定义空分隔符列表的 delims= 禁用此拆分行为。 tokens=* 与从文件名中删除前导 space 的结果不同。文件名可以以一个或多个 space 开头,尽管这很不常见。

文件名也可以包含感叹号 !,在使用延迟环境变量扩展时也必须考虑到这一点。每个文件名都传递给一个子程序以进一步处理它。

循环用于将所有出现的分配给循环变量 Year 的年份替换为圆括号中的年份,直到新文件名与当前文件名不同,因为替换对于搜索字符串确实是肯定的。 for /L %%J in (1980,1,2029) do ... 未使用,因为一旦在文件名中找到正确的年份就无法退出此循环。

在文件名中找到年份后,检查年份是否尚未嵌入括号中以避免将名称为 home vid (1987).mov 的文件重命名为 home vid ((1987)).mov。因此,例如 home video 1998.avi 最终重命名为 home video (1998).avi.

包含两个四位或更多数字的文件名也无法正确处理,因为此代码无法找出此类文件名中的年份。

这个批处理代码并不是真的很快,但它应该能在列出的限制下工作。

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • call /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • goto /?
  • if /?
  • ren /?
  • set /?
  • setlocal /?

另见

PS:名称中带有 () 的文件名使得使用批处理文件处理它们通常更加困难,因为在这种情况下,文件名必须始终包含在由于 () 而包含 space 字符的文件名的双引号对于 Windows 命令处理器 cmd.exe 也有特殊含义,因为它可以看出在上面的代码上。另见 How does the Windows Command Interpreter (CMD.EXE) parse scripts?