使用批处理脚本重命名多个文件



我有很多这种格式的文件:

D:imagesAAA_BBBimagewhatever1.jpg  
D:imagesAAA_BBBimagewhatever2.jpg
D:imagesFFF_EEE_CCCimageasdf1.jpg
D:imagesFFF_EEE_CCCimageasdf2.jpg
D:imagesWWW_XXX_YYY_ZZZimagesomeimage1.jpg
D:imagesWWW_XXX_YYY_ZZZimagesomeimage2.jpg

我想使用绝对文件路径的子字符串移动和重命名这些文件,并删除原始文件,以便结果看起来像下面这样,从0开始。

D:imagesAAAAAA_0.jpg
D:imagesAAAAAA_1.jpg
D:imagesEEEEEE_0.jpg
D:imagesEEEEEE_1.jpg
D:imagesYYYYYY_0.jpg  
D:imagesYYYYYY_1.jpg

请注意,带下划线的文件夹中的令牌数量可以是任意长度

下面的尝试只会给我从前面开始的第二个令牌。使用"令牌= 2,获取倒数第二个标记不会输出任何东西。

@echo off & setlocal EnableDelayedExpansion 
PushD "D:images" || Exit /B
For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL
^| %SystemRoot%System32findstr.exe /I /R  "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do For /F "Tokens=2 EOL=? Delims=_" %%H In ("%%G") Do (
Set "i=-1"
For /F "EOL=? Delims=" %%I In ('Dir "%%Gimage" /A:-D /B') Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
Ren "%%Gimage%%I" "%%H_!i!%%~xI" && (
If Not Exist "%%H." MD "%%H"
Move /Y "%%Gimage%%H_!i!%%~xI" "%%H"
)
EndLocal
)
)
PopD

然后我使用不同的逻辑,通过尝试两个for循环来获得最后一个令牌,然后是第二个最后一个令牌,但它不起作用。

@echo off & setlocal EnableDelayedExpansion 
PushD "D:images" || Exit /B
For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL
^| %SystemRoot%System32findstr.exe /I /R  "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do (
Set "dirName=%%G"
Set "lastSegment="
For %%H In ("!dirName:_=" "!") Do (
Set "secondLastSegment=!lastSegment!"
Set "lastSegment=%%~H"
)
Set "segment=%%~xG"
Set "i=-1"
For /F "EOL=? Delims=" %%I In ('Dir "%%Gimage" /A:-D /B') Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
Ren "%%Gimage%%I" "!secondLastSegment!_!i!!segment!" && (
If Not Exist "!secondLastSegment!." MD "!secondLastSegment!"
Move /Y "%%Gimage!secondLastSegment!_!i!!segment!" "!secondLastSegment!"
)
EndLocal
)
)
PopD

应该可以:

@echo off
setlocal EnableDelayedExpansion
pushd "D:images" || exit /B
for /D %%d in (*_*) do (
set "oldName=%%d"
for %%b in (".!oldName:_=.!") do for %%c in ("%%~Nb") do set "newName=%%~Xc"
set "newName=!newName:~1!"
md "!newName!"
set "i=-1"
for %%f in ("%%dimage*.jpg") do (
set /A i+=1
move "%%~f" "!newName!!newName!_!i!%%~Xf"
)
)

你的例子有很多不需要的或冗余的代码…

  • 一般来说,当你只想处理文件或文件夹时,不需要使用for /F命令;forfor /Dfor /F更简单,工作速度更快。
  • for /D %%d in (*_*) do命令处理所有至少有一个下划线的文件夹;我不明白为什么你使用findstr与一个复杂的正则表达式…
  • 参见我使用~Name~eXtensionFOR命令参数修饰符提取第二个姓氏的技巧。
  • 如果你处理每个源文件夹一次,你只需要创建相应的目标文件夹一次。您不需要检查每个文件中是否存在这样的文件夹。如果目标文件夹以前可能存在,您可以对每个源文件夹检查一次…

根据您提供的信息,这对您有帮助吗?

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "BaseDir=D:images"
PushD "%BaseDir%" 2>NUL || Exit /B
For /F "EOL=? Delims=" %%G In ('Dir "*_*" /A:D-L /B /O:N 2^>NUL
^| %SystemRoot%System32findstr.exe /IR "[^_][^_]*_[^_][^_]*"') Do (
PushD "%%G"
For /F "Tokens=1,* Delims=:" %%H In ('Dir "image*.jpg" /A:-D-L /B 2^>NUL
^| %SystemRoot%System32findstr.exe /INR ".jpg$"') Do (
For /F "Delims=_" %%J In ("%%G") Do (
If Not Exist "%BaseDir%%%J." MD "%BaseDir%%%J"
Move /Y "%BaseDir%%%Gimage%%~I" "%BaseDir%%%J%%J_%%H%%~xI"
) 1>NUL
)
PopD
)
PopD

请注意,您可能有空目录,您可能希望清理!

@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=u:images"
FOR /f %%e IN ('DIR /b /ad "%sourcedir%*_*"') DO (
rem %%e has firstlevel subdirectory name - must have at least one "_"
SET "L2=%%e"
FOR %%o IN (!L2:_^= !) DO SET "destsub=!prevpart!"&SET "prevpart=%%o"
FOR /f %%y IN ('dir /b /a-d "%sourcedir%%%eimage*.jpg"') DO (
MD "%sourcedir%!destsub!" >nul 2>NUL
SET "needtomove=Y"
FOR /L %%u IN (0,1 9999) DO IF DEFINED needtomove IF NOT EXIST "%sourcedir%!destsub!!destsub!_%%u.jpg" move "%sourcedir%%%eimage%%y" "%sourcedir%!destsub!!destsub!_%%u.jpg" >nul&SET "Needtomove="
)
)
GOTO :EOF

在应用于实际数据之前,始终针对测试目录进行验证。

查找包含_的每个子目录。通过用空格替换_s并使用for来查找倒数第二个以_分隔的元素。对于s子目录image中的每个*.jpg,尝试使其成为目标(2> null会抑制诸如"目录已经存在"之类的错误消息),然后尝试在目标目录中找到第一个可用的名称(因为不能保证目标中不存在流浪文件)并进行移动。

你的代码中有几个问题,我想消除:

  • 您的findstr过滤器过于严格,因此它只会让示例列表中的最后两个图像被处理。让我建议完全删除它,并使用目录掩码*_*代替。
  • 不需要尝试为每个映像文件创建目标目录;每个源目录一次尝试就足够了。
  • 通过if exist检查目录是否存在,当将.附加到路径时,会导致错误的结果(当它指向文件而不是目录时,它也会报告为存在);相反,*应该被附加,所以文件被报告为不存在。
  • 你应该为要处理的图像文件指定一个像*.jpg和/或*.png这样的文件名掩码,以便过滤掉意想不到的。
  • 也许你应该为要处理的图像文件指定一个排序顺序,否则,文件系统会决定顺序,这可能不是你期望的。
  • 变量segment将为空,因为%%~xG将为空(基于您的示例)。你很可能指的是%%~xI
  • 虽然技术上可行,但没有必要先重命名当前图像文件,然后将其移动到另一个子目录;一次移动操作就足够了。
  • 移动图像文件后,可能会留下空的子目录,应该清理。
  • 当使用setlocal进行环境本地化时,通常不需要使用pushd,因为setlocal也包含当前工作目录。请记住,与setlocal相比,缺少popd可能会留下不需要的pushd堆栈项,因为当批处理脚本终止时,endlocal总是隐式执行。
  • 不应该全局启用延迟变量扩展,因为文件名中包含!^可能会导致问题。

下面是修复了上述问题的代码(不涉及内在方法):

@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Usage of `pushd`/`popd` is usually not needed when using `setlocal/`endlocal`:
cd /D "%~dp0images" || exit /B
for /F "eol=| delims=" %%G in ('
rem/ // The `findstr` filter was too strong and is unnecessary ^(a simple `dir` is enough^)^^! ^& ^
2^> nul dir /B /A:D-H-S-L "*_*"
') do (
set "dirName=%%G"
set "lastSegment="
rem // Toggle delayed expansion to have it enabled only when necessary to not lose `!`-symbols:
setlocal EnableDelayedExpansion
for %%H in ("!dirName:_=" "!") do for %%J in ("!lastSegment!") do (
endlocal
set "secondLastSegment=%%~J"
set "lastSegment=%%~H"
setlocal EnableDelayedExpansion
)
rem /* Creation of the target directory is now done once per source directory
rem    (to check whether a directory exists, append `*` rather than `.`): */
if not exist "!secondLastSegment!*" md "!secondLastSegment!"
endlocal
set "targetDir=%%Gimage"
rem // Since `%%~xG` is going to be blank, `segment` will be empty and is therefore useless:
rem/ set "segment=%%~xG"
set /A "i=-1"
rem /* The `dir` should specify the file name mask(s) to exclude non-image files
rem    (a sort order is now specified; hidden, system and linked items are now excluded): */
for /F "eol=| delims=" %%I in ('cd /D "%%Gimage" ^&^& dir /B /A:-D-H-S-L /O:DN "*.jpg" "*.png"') do (
set /A "i+=1"
set "fileName=%%I"
rem // Variable `segment` is empty; use `%%~xI` instead:
set "fileExt=%%~xI"
setlocal EnableDelayedExpansion
set "newFileName=!secondLastSegment!_!i!!fileExt!"
rem/ ren "!targetDir!!fileName!" "!newFileName!" && (
rem The target directory has already been created at this point.
rem/ move /Y "!targetDir!!newFileName!" "!secondLastSegment!"
rem/ )


rem /* Instead of renaming and then moving, a single move operation suffices
rem    (note that variable `segment` is empty, hence use `%%~xI` instead): */
> nul move /Y "!targetDir!!fileName!" "!secondLastSegment!!newFileName!"
rem // Clean up potentially empty sub-directory:
2> nul rd "!targetDir!"
endlocal
)
rem // Clean up potentially empty source directory:
2> nul rd "%%G"
)
endlocal
exit /B

最新更新