用bat批量重命名不同文件夹下的同名文件:三种规则与脚本实战
前几天帮朋友整理移动硬盘一百多个文件夹都是从手机、相机、网盘里各自导出来的几乎每个文件夹里都躺着一张 IMG_0001.jpg还有一堆“新建文本文档.txt”。真要手动一个个改名字少说也得忙一整晚眼睛花掉还容易改错。我的第一反应就是用 bat 批量重命名遍历所有文件夹把文件所在目录的名字或路径拼到新文件名里才能一次性把不同文件夹下的同名文件彻底区分开。这篇文章把整个思路写完整包括三种可行的命名规则、可以直接抄的脚本代码以及我实际踩过之后总结的坑位给同样被这种重复命名折磨的朋友做个参考。1. 什么场景下会碰上“不同文件夹里的同名文件”1.1 我实际遇过的三种典型局面第一种是整理照片。手机、相机、无人机导出来之后设备通常会按日期建文件夹但每个文件夹里的照片序号都会从 0001 重新开始。于是就有了2023-07-15\IMG_0001.jpg、2023-07-16\IMG_0001.jpg、2023-07-17\IMG_0001.jpg这种组合。单看文件名全是同一个根本分不清是哪天拍的想挑几张发朋友圈都得先一层层点开确认日期。第二种是收集资料。平时从网站、U盘、微信里下载各种文档不同来源的文件经常都是“新建Microsoft Word文档.docx”“资料.doc”这种默认名字整理到一起之后全乱套。你想定位一份文件只能按记忆翻目录效率低得让人崩溃。更麻烦的是如果两批文件放进同一个文件夹后拷进来的同名文件会直接覆盖先拷的等发现的时候数据已经没了。第三种是把多个项目目录合并归档。每个项目里几乎都有config.ini、README.md、notes.txt这类固定名字一旦要汇总到一块儿文件名必然互相冲突。手动一个一个加前缀吧几十个项目还能忍几百个就完全不是人干的事。这三个场景的共同点很明确文件本身有价值但文件名失去了“辨识度”而人工重命名的工作量又太高。这时候就该让批量重命名脚本来干活。1.2 为什么这种需求用 bat 反而最顺手现在能批量改名的工具不算少Python 写个 os.rename 也很简单为什么我还要坚持用 bat第一bat 是 Windows 自带的不需要装 Python、不需要下载第三方软件。很多朋友只是临时整理一次文件为了一次性需求去配开发环境成本反而更高。第二bat 可以保存成.bat文件反复用下次再遇到同样需求双击运行就行不用重新找工具、重新点设置。第三批处理脚本的逻辑非常贴合“遍历目录 重命名”这种需求for 循环配合 ren 命令十几行就能写清楚出了问题也好排查。不否认遇到几百个目录、上万文件这种极端规模Python 和 PowerShell 确实更强。但对大多数日常整理场景bat 是最低门槛、最快见效的方案。至于.bat和.cmd的区别在这个用途里基本体会不出来你只需要知道 Windows 下双击能跑的批处理文件后缀用哪个都行。1.3 动手之前先定好“新名字”的生成规则写脚本之前建议先做一件事想清楚新文件名应该长什么样。规则没定好脚本写得再漂亮也白搭。我常用的规则有三种。第一种是“目录前缀法”把文件所在文件夹的名字拼到原名前面得到2023-07-15_IMG_0001.jpg这种结果适合按日期或主题分好类的情况。第二种是“顺序编号法”无视原来的名字统一改成0001_IMG_0001.jpg、0002_IMG_0001.jpg适合彻底重新整理的场景。第三种是“路径化命名法”用文件相对根目录的路径来生成新名字比如2023_07_15_001_IMG_0001.jpg这种设计可以保证全局唯一哪怕是两个同名子目录里的文件也不会冲突。选哪条规则取决于你后续怎么用这批文件。如果只是想把文件合并到一个目录里那路径化命名最稳如果还要按日期分类浏览目录前缀法更直观。总之在下手写 for 循环之前先把这条定义清楚。2. 写脚本前必须搞懂的两件事for 怎么遍历、延迟扩展怎么用2.1 for /d、for /r、for /f三种写法对应三种场景批处理里遍历文件夹和文件主力就是 for 命令。这一块必须讲透因为它直接决定了脚本能不能按预期找到要处理的文件。for /d %%d in (*) do (...)遍历当前目录的直接子文件夹%%d 取的是文件夹名不递归只看下一层目录。for /r %%f in (*.jpg) do (...)从当前目录开始递归遍历所有子目录逐个取出匹配*.jpg的文件%%f 是完整路径。for /f delims %%a in (命令) do (...)则是最灵活的一种它执行括号里的命令把输出逐行读出来给 %%a常用于把 dir 的排序结果、文件清单交给脚本处理。在这个重命名需求里for /r 负责找文件for /d 负责找文件夹for /f 加 dir 负责生成可控的文件清单三者各有各的位置。很多人一上来就用 for /r但遇到“需要按某种顺序处理”的场景就抓瞎了因为 for /r 的遍历顺序不完全可控。这时候改用 for /f dir 反而是更稳的选择后面编号方案里我会具体演示。2.2 变量延迟扩展批处理新手最常绕晕的机制很多人写 bat 批量重命名失败问题不在 ren 命令而在变量延迟扩展。批处理在执行一个复合语句块也就是for (...) do (...)里面那段整体时会先把整块内容“读一遍”把里头的%变量%全部替换成当时的值然后再真正执行。这意味着for 循环内部如果修改了一个变量、又想立刻读它直接写%变量%读到的是旧值。用我的话说批处理就像个“爱抄小抄的家伙”先看一眼现在是什么值然后整块抄下来之后变量怎么变他都当没看见。比如下面这段代码echo off set n0 for %%f in (a b c) do ( set /a n1 echo %n% )实际输出是三行0 0 0不是1 2 3。解决方法是文件开头加一行setlocal enabledelayedexpansion然后把循环里要动态读取的变量改成!n!这种写法echo off setlocal enabledelayedexpansion set n0 for %%f in (a b c) do ( set /a n1 echo !n! )这段就会输出1 2 3。在后面的编号方案里计数器必须用!n!否则永远读到 0改出来的名字全乱。这个知识点建议每次都多看一眼批量脚本里十有八九要碰到。2.3 参数扩展符用路径信息拼文件名就靠它for 循环拿到的变量比如 %%f通常代表一个完整路径。怎么从这个路径里拆出目录、文件名、扩展名这就要用到参数扩展符。下面这个表我基本每次写脚本都要翻干脆贴在这里。写法含义示例假设 %%f D:\photos\2023-07-15\IMG_0001.jpg%%~ff完整路径D:\photos\2023-07-15\IMG_0001.jpg%%~dpf盘符 目录结尾带反斜杠D:\photos\2023-07-15\%%~nxf文件名 扩展名IMG_0001.jpg%%~nf不带扩展名的文件名IMG_0001%%~xf仅扩展名含点.jpg%%~nxd目录名不含路径2023-07-15%%~dp0当前 bat 脚本所在目录D:\mytools\注意%%~dp0不是 for 变量而是对当前脚本路径 %0 的扩展常用于“切到脚本所在目录”。配合cd /d %~dp0能保证无论从哪个目录双击脚本处理的都是脚本旁边的内容而不是当前工作目录。这个习惯我一开始就养成了至少避免了八成“脚本跑完没变化”的困惑。3. 方案一把所在文件夹名直接并进文件名3.1 只处理当前目录下直接子文件夹的写法这是最直观也最容易上手的脚本适合那些子文件夹已经按日期、按主题分好但里面的文件名完全相同的情况。完整代码如下echo off setlocal enabledelayedexpansion cd /d %~dp0 rem 遍历当前目录下的每个子文件夹 for /d %%d in (*) do ( rem 判断这个子文件夹里是否存在 jpg 文件 if exist %%d\*.jpg ( rem 逐个处理 jpg 文件%%~nxf 取出原文件名 for %%f in (%%d\*.jpg) do ( rem 新文件名 文件夹名 下划线 原文件名 ren %%f %%d_%%~nxf ) ) ) echo 处理完成 pause执行逻辑不复杂外层for /d拿到每个子文件夹的名称if exist先看看里面有没有 jpg有就用内层for %%f逐个处理。%%d_%%~nxf中文件夹名和原文件名都被拼进新名字只要同一父目录下没有两个同名子文件夹生成的名称就不会互相冲突。同一父目录下不可能有两个同名子文件夹所以这个方案在“只处理一层子文件夹”的范围内是安全的。如果想换个文件类型把所有.jpg替换成.png、.txt都行脚本结构不用变。这一步我建议先复制一个测试目录跑一遍亲眼看看结果是预期的再放到真实目录上执行。3.2 递归处理所有层级子文件夹的写法有时候文件嵌套得更深比如2023\07\15\IMG_0001.jpg这时候只处理一层就不够了。我推荐用dir /ad /b /s生成目录清单再逐个进去处理echo off setlocal enabledelayedexpansion cd /d %~dp0 for /f delims %%d in (dir /ad /b /s 2^nul) do ( if exist %%d\*.jpg ( for %%f in (%%d\*.jpg) do ( ren %%f %%~nxd_%%~nxf ) ) ) echo 处理完成 pause这里dir /ad /b /s会列出所有子目录的完整路径而且不包含起点目录本身省去了用 for /r /d 时还要手动跳过根目录的麻烦。%%~nxd拿的是当前目录的基本名比如2023-07-15。当一个目录里有 10 张 jpg它们会变成2023-07-15_IMG_0001.jpg、2023-07-15_IMG_0002.jpg目录之间因为目录名不同新文件名也不会撞。3.3 这个方案的两个“漏网之鱼”边界方案一看着简单但有两个边界情况必须提前知道。第一个是“不同层级的同名目录”。比如A\pic和B\pic这两个目录都叫pic递归处理时%%~nxd都是pic生成的pic_xxx.jpg虽然目前不在同一目录里但如果你后续要把它们合并到一个文件夹还是会重名。要彻底避免就得用第 4 章的路径化命名。第二个是“重复执行导致重名”。脚本跑过一次之后再跑第二次时目录里已经存在2023-07-15_IMG_0001.jpg而原文件IMG_0001.jpg已经不存在了所以正常情况下不会冲突。但如果你中途改过文件或者目录里本来就有2023-07-15_IMG_0001.jpgren 就会报“存在一个重名文件”。稳妥的做法是在 ren 前加一层判断if not exist %%d_%%~nxf ( ren %%f %%d_%%~nxf ) else ( echo 跳过: %%d_%%~nxf 已存在 )这样既不会中断脚本也能把冲突的文件列出来方便你人工决定下一步。4. 方案二顺序编号与“路径化”命名解决同名冲突和排序4.1 稳定编号补零 dir 排序避免 1、10、100 乱序如果不在乎原来的文件名只想让所有文件按固定顺序重新编号可以用顺序编号方案。这个方案的难点有两个计数顺序和补零。先看代码echo off setlocal enabledelayedexpansion set n0 for /f delims %%f in (dir /b /s /a-d /o:n *.jpg 2^nul) do ( set /a n1 set num000!n! set num!num:~-4! ren %%f !num!_%%~nxf ) echo 共处理 !n! 个文件 pause这里用dir /b /s /a-d /o:n而不是 for /r核心原因是 dir 的遍历顺序更可控/o:n按名称排序拿到文件清单后逐个处理。set num000!n!加set num!num:~-4!是把 1 变成0001、把 12 变成0012。如果不补零10 个文件后排序就会变成1、10、100、2、20这种鬼样子文件管理器里看着非常难受。这个方案的优点是彻底统一了文件名的“外观”缺点是原来的文件名信息完全丢失了。所以我用它的时候一般会在重命名前先把清单导出来留底或者在新文件名里保留原名比如!num!_%%~nxf既保证顺序又能看到原始文件名。4.2 路径化命名用相对路径拼出新文件名天然唯一路径化命名是我个人最推荐的办法尤其适合“多个子文件夹里都有同名文件后续还要合并”的场景。思路很简单用文件相对根目录的完整路径来生成新文件名把路径分隔符换成下划线这样每个文件的新名字天然唯一。echo off setlocal enabledelayedexpansion cd /d %~dp0 set root%~dp0 set root!root:~0,-1! for /r %%f in (*.jpg) do ( set dir%%~dpf set dir!dir:%root%! set dir!dir:\_! set dir!dir:~0,-1! ren %%f !dir!_%%~nxf ) echo 处理完成 pause执行过程拆开看就是这样假设根目录是D:\photos遇到文件D:\photos\2023-07-15\IMG_0001.jpg先取它的目录部分D:\photos\2023-07-15\然后把根目录前缀D:\photos替换掉得到\2023-07-15\再把反斜杠换成下划线变成_2023_07_15_最后去掉开头的下划线拼上原文件名得到2023_07_15_IMG_0001.jpg。这个方案最稳妥的地方在于它把完整目录层级都编码进了文件名。哪怕两个不同路径下的子文件夹同名只要它们的完整相对路径不同生成的文件名就不会重复。代价是当目录层级很深时新文件名会比较长但大多数整理场景完全能接受。用这个写法时有个前提脚本所在目录里尽量不要有!和%这两个字符。set dir!dir:%root%!这行在做变量替换时特殊字符可能被解释轻则替换失败重则整个路径被改坏。我遇到过一次根目录名里带!的情况跑完大量文件被改成乱码从那以后我都要求脚本所在目录的路径只包含字母、数字、中文和下划线。4.3 文件特别多的时候要留意什么几千个文件纯 bat 完全能跑但速度取决于磁盘 IO 和控制台输出。如果每个文件都echo一条到屏幕执行时间会明显拉长建议处理完只打一行统计结果。如果文件数量到了几万级批处理的性能就会吃紧。这时候我更推荐直接切到 PowerShell用Get-ChildItem配Rename-Item性能和处理复杂逻辑的能力都会好很多。bat 的价值在于短平快规模一大要学会及时换工具这也是我踩过几次坑之后的体会。5. 不改错文件才是第一优先级安全措施与坑位排雷5.1 干跑模式先看结果再执行改文件名的操作是不可逆的一旦改错再想恢复就得靠记忆或者备份。所以我写批量重命名脚本时会强制自己加一个干跑模式把ren换成echo ren脚本先跑一遍屏幕打印出所有将要执行的命令我逐个检查新文件名是否符合预期确认无误后再去掉干跑开关正式执行。最简单的做法是改一行。比如set dry1 ... if %dry%1 ( echo ren %%f !num!_%%~nxf ) else ( ren %%f !num!_%%~nxf )dry 设为 1 时只打印命令不执行检查完改成 0再跑一遍就是真改。这个方法虽然土但能拦住大部分低级错误比如变量替换没写对、新名字里带了非法字符、目录前缀拼错等等。干跑模式下我还会顺手把完整清单导出来一份万一后面真出问题至少知道原来的文件名是什么。5.2 中文乱码、空格、感叹号……这些隐形炸弹逐个拆中文乱码是重命名脚本翻车率最高的一类。用记事本保存 bat 时编码务必选“ANSI”在简体中文 Windows 下就是 GBK。如果保存成 UTF-8cmd 用 GBK 代码页去解析中文字符串会变成乱码cd /d和ren都可能找不到目标。脚本开头加一行chcp 936 nul能强制切到中文代码页算是个保险。文件名带空格的问题几乎所有人第一次都会踩。ren 命令的两侧参数必须都用引号包起来写成ren D:\my files\a 1.jpg a 1_new.jpg。如果新名字里有空格但没加引号ren 会报“命令语法不正确”而且不会告诉你错在哪一行。感叹号则是延迟扩展的深坑。一旦开启setlocal enabledelayedexpansion批处理会把!当成变量边界文件名里的!可能被吞掉导致文件找不到或者名字被改坏。遇到这种情况稳妥的办法是处理前先用dir /b | findstr !看看有没有带感叹号的文件有就先移走或者干脆用 PowerShell 处理这类文件。别想着硬绕绕来绕去只会更乱。文件被占用也要留意。如果某个 jpg 正被看图软件打开ren 会报“另一个程序正在使用此文件”改名前先把可能占用文件的程序都关掉能省很多排查时间。5.3 脚本双击就闪退到底怎么排查闪退通常不是脚本坏了而是错误信息一闪而过。我排查的方法按顺序来基本能解决 90% 的问题。第一步打开命令提示符把 bat 文件直接拖进去回车在窗口里跑任何错误信息都能停在屏幕上。第二步在脚本需要观察的位置加pause最后一行也加pause双击时就不会直接关窗口。第三步如果还定位不到把第一行改成echo on也就是去掉 off让 cmd 把每一条执行的命令都打印出来对照着看哪一步参数不对。第四步用setlocal包裹脚本让环境变量在脚本结束时自动恢复不污染当前 cmd 会话。另一个常见新手坑是脚本放在带空格的路径里cd /d %~dp0又没加引号导致脚本目录切不进去后面所有相对路径全部失效。加引号这个操作我在每个脚本示例里都写了不是啰嗦是真的太容易忘。6. 把脚本升级成拿起来就能用的通用小工具6.1 用菜单接收输入扩展名、处理模式都让用户选脚本写多了就会发现每次都手改扩展名、手改规则很烦。干脆做成菜单式双击后自己选。一个简化版的结构是这样echo off setlocal enabledelayedexpansion chcp 936 nul cd /d %~dp0 echo 当前根目录: %cd% set /p ext请输入要处理的扩展名(默认jpg): if %ext% set extjpg echo. echo 请选择处理方式 echo 1. 加目录名前缀 echo 2. 顺序编号 echo 3. 路径化命名 set /p mode请选择 1/2/3: if %mode%1 goto mode1 if %mode%2 goto mode2 if %mode%3 goto mode3 goto end后面接对应分支的循环体就行。虽然比单一脚本长一点但胜在一劳永逸以后不管整理照片还是整理文档双击、输入、回车三步搞定。6.2 把每次改名记录下来关键时刻能回滚批量重命名最怕的就是执行完才发现规则错了又忘了原来的文件名。所以我写完脚本后一定会加日志功能在每次 ren 之前把旧名和新名写入一个文本文件。echo %%f --^ !newname! %~dp0\改名记录.txt如果处理的文件多这个记录文件会很大但用起来是真的安心。更进一步可以在最后生成一个undo.bat把 ren 命令反向写进去出错时双击它就能一键还原。生成方式很简单echo ren !newname! %%~nxf %~dp0\undo.bat需要提醒的是日志和 undo 文件如果放在脚本根目录里而你的处理目标包含*.txt或*.bat它们可能被后续遍历扫到导致把自己也改名了。建议日志和回滚脚本固定放在脚本所在目录并且处理范围不要包含该目录。6.3 拖拽文件夹到 bat 上直接处理最后分享一个我特别喜欢的交互设计用%~1接收拖进来的文件夹。把 bat 放在桌面日常使用时直接把某个文件夹拖到 bat 图标上脚本就自动切换到那个目录再处理。set target%~1 if %target% set target%cd% cd /d %target%这样就不用每次改脚本里的路径也不用担心忘记“脚本放哪就处理哪”的约定。我对这个设计的依赖程度已经到了“没它就不想用 bat”的地步强烈推荐你也试试。至于这个“批量重命名不同文件夹下同名文件”的需求还能怎么扩展我的建议是先在小范围测试目录里把规则跑顺再上真实数据重要数据提前备份脚本里所有路径和文件名在引用时都加引号。把这三点记在心里你不仅能改 jpg还能顺手处理各种文档、压缩包、音频文件一套思路通吃。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →