求助:使用Batch脚本批量重命名并移动文件的实现问题
问题描述
我有一批格式如下的文件:
D:\images\AAA_BBB\image\whatever1.jpg D:\images\AAA_BBB\image\whatever2.jpg D:\images\FFF_EEE_CCC\image\asdf1.jpg D:\images\FFF_EEE_CCC\image\asdf2.jpg D:\images\WWW_XXX_YYY_ZZZ\image\someimage1.jpg D:\images\WWW_XXX_YYY_ZZZ\image\someimage2.jpg
希望通过提取文件路径的子串,移动并重命名这些文件(原文件自动删除),最终效果如下(序号从0开始):
D:\images\AAA\AAA_0.jpg D:\images\AAA\AAA_1.jpg D:\images\EEE\EEE_0.jpg D:\images\EEE\EEE_1.jpg D:\images\YYY\YYY_0.jpg D:\images\YYY\YYY_1.jpg
注意:带下划线的文件夹中,下划线分割的分段数量可以是任意长度。
我尝试了以下脚本,它只能提取开头数第2个分段,用Tokens=-2获取倒数第2个分段没有输出:
@echo off & setlocal EnableDelayedExpansion PushD "D:\images" || Exit /B For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL ^| %SystemRoot%\System32\findstr.exe /I /R "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"' ) Do For /F "Tokens=2 EOL=? Delims=_" %%H In ("%%G") Do ( Set "i=-1" For /F "EOL=? Delims=" %%I In ('Dir "%%G\image" /A:-D /B') Do ( Set /A i += 1 SetLocal EnableDelayedExpansion Ren "%%G\image\%%I" "%%H_!i!%%~xI" && ( If Not Exist "%%H\." MD "%%H" Move /Y "%%G\image\%%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%\System32\findstr.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 "%%G\image" /A:-D /B') Do ( Set /A i += 1 SetLocal EnableDelayedExpansion Ren "%%G\image\%%I" "!secondLastSegment!_!i!!segment!" && ( If Not Exist "!secondLastSegment!\." MD "!secondLastSegment!" Move /Y "%%G\image\!secondLastSegment!_!i!!segment!" "!secondLastSegment!" ) EndLocal ) ) PopD
解决方案
原脚本的问题主要有三点:
- 第一个脚本的
findstr正则限定了文件夹必须至少有3个下划线,导致AAA_BBB这类只有1个下划线的文件夹被过滤,根本没处理; - 第二个脚本里
Set "segment=%%~xG"是错误的,%%G是文件夹名,没有扩展名,应该取原文件的扩展名%%~xI; - 嵌套的
SetLocal/EndLocal会导致变量在内部被重置,无法正确读取外部定义的secondLastSegment。
以下是修正后的脚本,能正确提取文件夹名的倒数第二个下划线分割段,并完成文件的移动和重命名:
@echo off & setlocal EnableDelayedExpansion PushD "D:\images" || Exit /B :: 遍历所有包含下划线的子文件夹 For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL ^| findstr /I "_"') Do ( Set "dirName=%%G" Set "prevSegment=" Set "currSegment=" :: 分割文件夹名,提取倒数第二个分段 For %%H In ("!dirName:_=" "!") Do ( Set "prevSegment=!currSegment!" Set "currSegment=%%~H" ) :: 处理只有一个下划线的文件夹(分割后仅2段) If "!prevSegment!"=="" Set "prevSegment=!currSegment!" Set "i=-1" :: 遍历当前文件夹下image子目录里的文件 For /F "EOL=? Delims=" %%I In ('Dir "%%G\image" /A:-D /B 2^>NUL') Do ( Set /A i += 1 :: 目标文件夹不存在则创建 If Not Exist "!prevSegment!\." MD "!prevSegment!" :: 一步完成移动和重命名 Move /Y "%%G\image\%%I" "!prevSegment!\!prevSegment!_!i!%%~xI" ) ) PopD endlocal
关键说明:
- 移除了原脚本中限制下划线数量的正则,改为只要包含下划线的文件夹都处理,符合“分段数量任意”的需求;
- 通过遍历分割后的所有分段,记录前一个分段作为倒数第二个分段;如果文件夹只有一个下划线,则直接取第一个分段,匹配最终效果要求;
- 用
Move命令一步完成移动和重命名,省去先改名再移动的步骤,避免中间出错; - 移除不必要的嵌套
SetLocal/EndLocal,确保变量能正确传递。
内容的提问来源于stack exchange,提问作者Beef_tataki12
相关产品推荐
相关产品推荐

