☰
PowerShell批量重命名MP4文件:正则提取歌手歌曲名实战
2026/10/14 15:31:23 网站建设 项目流程

1. 需求拆解与方案选型:一个播放列表文件夹引发的整理工程

先说点实在的。如果你手头有一大批MP4文件,文件名长这样——“歌手A - 歌曲名一.mp4”“歌手B - 歌曲名二.mp4”,而它们全都堆在一个叫“Beautiful Chinese Music Selected Playlist”的文件夹里,当你看着播放器里乱七八糟的标题列表、想着以后怎么按歌手快速检索时,你就知道这次不能再手动一个个改了。

这个场景的核心需求其实有三个:批量提取、规则重命名、不破坏文件。所谓“提取歌手名和歌曲名”,指的就是从MP4文件名本身解析出“歌手”和“歌曲”两个信息字段,然后按统一的命名规则重新组织文件名,比如统一成“歌手 - 歌曲名.mp4”或者反过来。这里的关键前提是:文件名里已经包含了歌手和歌曲信息,只是格式不规范,或者被各种下载工具加上了多余前缀后缀(比如“[www.example.com]”“_HD”之类),导致播放器显示混乱。

那问题来了:为什么不用现成的批量重命名工具?说实话,市面上有不少图形化批量改名工具,像某知名文件管理器自带的重命名工具、各种免费批量改名软件,都能做查找替换、加序号这类操作。但这类工具在处理“拆分字段后重新组合”的需求时往往很别扭:有的不支持正则,有的正则支持得很蹩脚,有的重命名几百个文件时试运行预览和实际结果不一致。更关键的是,它们很少能让你灵活定义“从文件名中提取哪几段、再按什么顺序拼回去”这样的逻辑。

我自己用下来的结论是:这种结构化提取 + 重命名的任务,用脚本才是最顺手的。脚本可以选择Python,也可以选择PowerShell。之所以我这次选了PowerShell,是因为它在Windows环境下的几个实打实的优点:

  • 系统自带,不需要安装Python解释器,也不需要处理pip依赖。
  • 对中文文件名的支持很稳,配合正则处理UTF-8/UTF-16文件名都正常。
  • Get-ChildItem配合Rename-Item可以直接对文件对象操作,不需要频繁拼接路径。
  • 出错时能拿到对象级别的异常信息,排查方便。

还要澄清一点:这个“PS”指的是PowerShell,不是Photoshop。我看到这个标题时第一反应也愣了一下,但做批量文件重命名这件事,用PhotoShop纯属拿大炮打蚊子,PowerShell才是正解。如果你手头正好有一批从音乐软件、素材站或朋友那收集来的MP4音乐文件,文件名格式五花八门,那这篇文章的脚本逻辑可以直接抄,改一改文件夹路径和正则规则就能用。

2. 核心逻辑拆解:正则提取与命名规则的设计思路

2.1 文件名模式分析:先弄清楚你的输入长什么样

在写任何代码之前,第一步永远不是写代码,而是先手动看20个文件名。这一步我吃了不少亏,曾经以为文件名格式很统一,结果跑了两次批量改名,最后发现有一批文件根本没匹配上,白折腾。

以“Beautiful Chinese Music Selected Playlist”这类文件夹为例,我实际遇到的典型文件名模式大致有这几种:

  • 歌手A - 歌曲名一.mp4(标准模式,中间有空格加连字符)
  • 歌手A-歌曲名一.mp4(没有空格,直接连字符)
  • 歌手B_歌曲名二.mp4(下划线分隔)
  • 【歌手C】歌曲名三.mp4(中文方括号包裹歌手名)
  • 歌手D - 歌曲名四 (Official Music Video).mp4(带多余后缀标签)
  • 未知歌手 - 歌曲名五 1080p.mp4(带清晰度标记)

如果文件名来源比较杂乱,甚至还会出现“歌曲名 - 歌手.mp4”这种反向排列的情况。所以命名规则设计的第一步,就是确定你的输入格式到底是什么。最简单的做法是:跑一段扫描脚本,把目录下所有文件名打印出来,人工过一遍,归纳出共性。这一步决定了你的正则能不能一次匹配90%以上的文件。

我在这个项目里的做法是先把所有文件列出来,手动扫了一遍。发现大约85%的文件都是“歌手 - 歌曲名.mp4”这种标准模式,剩下的要么是多了标签后缀,要么是分隔符不统一。这个统计很重要,因为它决定了我后续是写一套正则通吃,还是写两套正则以备兼容。

2.2 正则表达式怎么写:提取字段的细节与陷阱

确定了输入格式是“歌手 - 歌曲名”之后,核心就是一个正则表达式。PowerShell里正则匹配用的是-match运算符,结果存放在自动变量$matches里。

一个典型的基础正则长这样:

$pattern = '^(.*?)\s*-\s*(.*?)\.mp4$'

其中^(.*?)是非贪婪匹配,尽量少地匹配字符;\s*-\s*表示连字符前后可以有空格也可以没有;(.*?)\.mp4$匹配歌曲名直到扩展名结束。比如文件名“歌手A - 歌曲名一.mp4”,这个正则提取到的$matches[1]是“歌手A”,$matches[2]是“歌曲名一”。

但注意一个陷阱:(.*?)这种非贪婪写法在PowerShell的-match里是默认从左到右匹配的,一旦遇到不符合规则的字符就可能导致误提取。比如文件名“歌手A - 歌曲名一 (Live).mp4”,在歌曲名位置会提取出“歌曲名一 (Live)”,这个结果本身没错,但后处理时需要决定是否去掉括号里的演出标签。

如果输入文件里同时存在“歌手A-歌曲名一.mp4”和“歌手A - 歌曲名一.mp4”这两种模式,上面那个正则已经兼容了,因为\s*-\s*的意思就是“连字符两边可以有任意数量的空格”。但如果遇到下划线分隔的文件名“歌手B_歌曲名二.mp4”,就需要改成匹配_或-的分隔符:

$pattern = '^(.*?)[_\-\s]+(.*?)\.mp4$'

这里用[_\-\s]+表示分隔符可以是下划线、连字符、空白字符中的一个或多个。但这里又会出现一个问题:如果歌手名或者歌曲名本身带空格(比如“Various Artists - New Song.mp4”),这种正则就会错误地在空格处断开。所以没有万能正则,只有针对你的文件名统计结果设计的最优正则。这也是为什么前面强调要先看一遍文件名,而不是直接抄一段代码就开跑。

还有一个容易被忽视的点:正则中的\.必须转义,因为.在正则里代表任意字符。如果你写(.*?).mp4$,它也能工作,但会把“歌曲名Xmp4”这种缺少点号的文件名也匹配进来,这就是一些批量改名脚本会莫名其妙改错文件的原因之一。

2.3 命名规则设计:为什么我统一命名为“歌曲名 - 歌手.mp4”

提取出歌手和歌曲名之后,下一步是决定新文件名格式。很多人第一反应是保持原格式“歌手 - 歌曲名.mp4”,毕竟看着顺眼。但我在实际使用中发现,音乐播放器和手机视频App的排序逻辑往往按文件名的第一个字符走。如果首字符是歌手名,同一歌手的歌曲会被聚在一起,可一旦文件多了,每个歌手的歌曲之间又被其他歌手的名字插开,想看某个歌手的所有视频反而要翻页找。

反过来,如果命名成“歌曲名 - 歌手.mp4”,文件名首字符是歌曲名,很多播放器按名称排序时就会把所有歌曲按名称字母排序,视觉上更接近“专辑曲库”的顺序。当然,这个取舍看个人习惯,没有标准答案。关键是在脚本开始运行前把规则定下来,并且集中在一处定义,不要在脚本里到处写死。

我最终定的规则是:

  • 干净版本:歌曲名 - 歌手.mp4
  • 如果歌手名未知或为空:歌曲名.mp4(不再保留多余的“未知歌手”字样)
  • 如果文件名里带“1080p”“HD”“Official”这类后缀标签:提取歌手和歌曲名时直接剥离

这个规则看起来简单,实际操作时却要处理几个边界:万一歌曲名里本身包含“ - ”怎么办?万一同一个歌手有大量同名歌曲怎么办?这属于重名冲突问题,后面第4章会专门讲处理方案。命名规则设计时一定要预想“同名文件”和“文件名长度超过Windows限制”这两种极端情况,否则批量脚本跑到一半报错,或者把文件改成同名后互相覆盖,那损失就大了。

3. 完整PowerShell脚本:从扫描到重命名的全流程实现

3.1 脚本整体结构与工具函数

把需求理清楚后,我写的PowerShell脚本大致分成六个阶段:定义目录、扫描文件、正则匹配、提取字段、试运行预览、正式重命名。推荐把“试运行预览”单独拿出来作为一个开关参数,这比我以前直接跑简直提升了不止一个安全等级——你永远不知道真实文件名里藏着什么意外。

先看脚本框架:

param( [string]$Directory = "C:\Users\Public\Music\Beautiful Chinese Music Selected Playlist", [switch]$WhatIf = $false ) $ErrorActionPreference = "Stop" $logPath = Join-Path $Directory "rename_log.csv" $files = Get-ChildItem -Path $Directory -Filter *.mp4 -File

这里的param块允许你在命令行传入目录路径,不用每次改脚本里的硬编码路径。WhatIf开关是用来做“试运行”的,也就是预览将要发生的改动而不实际执行,这个参数后面细讲。

特别注意一行:$ErrorActionPreference = "Stop"。这行的意思是:一旦某个命令发生错误,立即停止整个脚本,而不是让PowerShell默认“非致命错误继续执行”的行为。批量重命名这类有破坏性操作,一旦中途出错,最好停下来让你人工确认,而不是带着错误状态继续把文件都改了。这一点我在早期脚本里没加,结果某次因为两个文件重名,Rename-Item报了个非终止错误,脚本继续跑完后一半文件改乱了,排查半天才恢复。

3.2 从文件名提取歌手与歌曲名的核心实现

提取这部分是整篇文章最核心的代码。我用了一个foreach循环遍历所有MP4文件,然后对每个文件名做正则匹配。匹配成功的提取字段,匹配失败的单独记录到另一个日志文件,绝对不静默跳过。

$results = foreach ($file in $files) { $baseName = $file.BaseName $pattern = '^(.*?)\s*-\s*(.*)$' if ($baseName -match $pattern) { $artist = $matches[1].Trim() $title = $matches[2].Trim() # 清理常见的多余后缀标签 $title = $title -replace '\s*\((Official\s*)?(Music\s*)?Video\s*\)\s*$', '' $title = $title -replace '\s*(1080p|720p|HD)\s*$', '' # 生成新文件名,统一为"歌曲名 - 歌手.mp4" $newName = "{0} - {1}.mp4" -f $title, $artist [PSCustomObject]@{ OldName = $file.Name NewName = $newName Artist = $artist Title = $title } } else { Write-Warning "未能匹配的文件: $($file.Name)" [PSCustomObject]@{ OldName = $file.Name NewName = "" Artist = "" Title = "" } } }

这段代码里有几个细节值得展开。

第一个细节是$matches[1].Trim()和$matches[2].Trim()。正则里我已经处理了连字符前后的空格(\s*),但Trim()依然是必要的:万一某文件名是“歌手A - 歌曲名”(多个空格),非贪婪匹配到的字段边界可能残留一个空格。这是低成本保险。

第二个细节是标题清理的正则。-replace '\s*\((Official\s*)?(Music\s*)?Video\s*\)\s*$', ''这个表达式把“ (Official Music Video)”“ (Music Video)”这类常见后缀去掉。写这类正则时一定要在末尾加$锚点,否则会把歌名中间的“ (Live)”也误删。每次加清理规则都建议先跑一遍测试,再放到批处理里。

第三个细节是输出一个PSCustomObject对象,而不是直接重命名。这意味着脚本先扫描并计算所有新文件名,把结果保存下来,人肉检查没问题之后再执行重命名。这个“计算和操作分离”的思路,是避免批量重命名事故的最好方式。

3.3 试运行与正式执行:安全重命名的两个阶段

我强烈建议任何重命名脚本都内置一个“试运行”模式。PowerShell的-WhatIf参数可以直接传给Rename-Item,它会报告“如果执行会怎么样”而不会真的去改文件。放到脚本里就是:

if ($WhatIf) { Write-Host "=== 试运行模式,不会实际重命名 ===" -ForegroundColor Yellow $results | Where-Object { $_.NewName -ne "" } | Format-Table OldName, NewName -AutoSize } else { $results | Where-Object { $_.NewName -ne "" } | ForEach-Object { $oldPath = Join-Path $Directory $_.OldName $newPath = Join-Path $Directory $_.NewName if (Test-Path -LiteralPath $newPath) { Write-Warning "目标文件已存在,跳过:$($_.NewName)" } else { Rename-Item -LiteralPath $oldPath -NewName $_.NewName } } }

这里Test-Path判断新路径是否已存在,防止两个文件被改成同一个名字时发生覆盖。这是批量重命名里最基础也最重要的防呆检查。目标文件已存在时跳过并报警告,比覆盖后追悔莫及要强得多。

执行时先跑一次试运行,把几十个“旧名字 -> 新名字”的对应关系扫一遍,确认没有离谱的转换结果后,再真实执行。整个流程大概两三分钟就能完成。

3.4 生成操作日志:给后续排查留个后门

重命名完成后,把操作记录导出成一个CSV日志文件。不要小看这一步。我经历过一次用其他工具批量改名后想撤销,因为没有日志,根本不知道哪些文件原来叫什么,恢复了很久才搞定。有了日志,将来反悔时可以直接按日志反向操作。

$results | Export-Csv -Path $logPath -NoTypeInformation -Encoding UTF8 Write-Host "处理完成,日志已保存到: $logPath" -ForegroundColor Green

在$WhatIf模式下不要写这个日志,因为实际并没有改动。真实执行后写日志,并且日志里记录OldName和NewName两个字段,这就是完整的操作审计。如果你将来想更进一步,甚至可以记录RenamedAt时间戳字段,但我个人觉得没必要,除非你在一个团队环境里管理共享素材库。

3.5 进阶补充:从文件内部元数据读取歌手信息

如果某个场景下文件名里根本没有歌手信息,或者文件名里的歌手名和MP4文件内部的元数据不一致,上述基于文件名的方案就失效了。这种情况要直接从文件内部读取元数据。

Windows系统有一个自带COM组件Shell.Application,可以读取MP4等媒体文件的元数据属性,包括艺术家、标题等。PowerShell调用它非常轻量,不需要装第三方库:

$shell = New-Object -ComObject Shell.Application $folder = $shell.Namespace($Directory) $fileItem = $folder.ParseName("歌手A - 歌曲名一.mp4") $artist = $folder.GetDetailsOf($fileItem, 13) # 13 通常对应"艺术家"属性 $title = $folder.GetDetailsOf($fileItem, 21) # 21 通常对应"标题"属性

这里属性索引号根据不同的Windows版本有差异,13通常是“艺术家”,21通常是“标题”,但最好先在本机测试一下索引对应关系。这个方法的好处是无论文件名怎么变,都能从MP4内部Tag提取标准字段;坏处是如果MP4本身没写入元数据(比如从某些网站直接下载的文件),那也读不出来。所以我一般把方案顺序定为:优先从文件名提,提不到时再尝试元数据提,两个都拿不到就归为“需要人工处理”的文件。

4. 常见问题与排查技巧实录

4.1 匹配失败:为什么有些文件名就是提取不出来

最常见的匹配失败原因有两个:一个是文件名模式超出了正则预设,另一个是文件名的扩展名判断出了问题。

比如文件名“歌手A feat. 歌手B - 歌曲名.mp4”里,feat.这个缩写被某些播放器写作“feat”不带点,有的写作“ft.”。如果你的正则用了^(.*?)\s*-\s*(.*?)\.mp4$,这个文件其实还是能匹配的,歌手名会是“歌手A feat. 歌手B”,这没问题。真正匹配失败的是这种:“歌手A_歌手B_歌曲名.mp4”,因为分隔符是下划线,而且没有“ - ”结构,那就需要单独的正则或手工处理。

排查方法很简单:脚本里我已经写了Write-Warning "未能匹配的文件: ...",把打印出来的文件名收集起来再看一眼,统计规律,需要的话给这些特殊模式加第二套正则。不要试图写一个万能正则,那只会让正则难以维护。

4.2 重名冲突与Windows文件名限制

重名冲突的处理原则是:绝不覆盖,先跳过,人工处理。我在脚本里用Test-Path做了判断,只要目标文件已经存在就跳过。批量处理时如果出现大量重名,极其有可能是命名规则设计有缺陷——比如“歌曲名 - 歌手.mp4”如果同一歌手对同一首歌有多个不同版本,就会冲突。这时候可以在新文件名里追加序号后缀,比如“歌曲名 - 歌手 (1).mp4”,但前提是要判断同名数量,这会让脚本复杂度增加不少。作为初版方案,先跳过重名、人工处理反而更安全。

Windows文件名长度上限是255个字符左右(完整路径路径字符数限制约260个字符)。中文文件名每个字算一个字符,看起来不太容易超,但如果歌曲名特别长(有些现场版歌名动辄几十个字),加上目录路径和歌手名,可能触发系统限制。遇到这种情况,Rename-Item会抛异常,脚本里因为设置了$ErrorActionPreference = "Stop"就会暂停运行,这时再人工处理该文件名,比如截断歌曲名长度。

4.3 文件占用与只读属性

重命名时如果文件正被播放器或下载工具占用,会抛出正在使用异常。建议在跑批处理之前,关掉所有可能挂着该文件夹的媒体播放器——我踩过这个坑:VLC开着播放列表,批量改到一半报错,还以为是脚本Bug,排查了半天才发现是VLC锁住了文件。

只读文件理论上可以重命名,但如果是“受保护的系统文件”或隐藏文件,Get-ChildItem -Filter *.mp4默认不会包含它们。如果需要处理隐藏文件,要加-Force参数。不过对正常音乐素材文件来说,这个场景很少出现。

4.4 常见问题速查表

问题现象可能原因排查与处理
某些文件未被匹配文件名格式与预设正则不符打开日志中“未能匹配的文件”列表,人工检查,必要时补充第二套正则
改名后文件变成乱码PowerShell控制台编码与文件系统不一致在PowerShell窗口执行[Console]::OutputEncoding = [Text.Encoding]::UTF8,或检查脚本文件本身是否以UTF-8编码保存
目标文件名已存在两个源文件提取后生成相同的新名称脚本已跳过并报警告,人工决定删除旧重复文件或追加序号
脚本报“路径太长”新文件名+目录路径超过Windows限制人工截断超长歌曲名,或把文件移动到更浅的目录层级
运行后文件没变化WhatIf模式没有关闭检查命令行是否误加了-WhatIf参数,脚本默认模式是正式执行
文件正在使用无法重命名播放器或编辑器占用文件关闭相关程序,重新运行脚本
日志CSV打开乱码编码不是UTF-8 with BOMPowerShell导出CSV时建议加-Encoding UTF8,Excel打开还乱码就改用记事本中转,或用文本编辑器打开

4.5 几个实测下来很值钱的习惯

最后一个经验。批处理重命名这种事,我做了很多次,现在总结出三条习惯,每次执行都让我少踩不少坑:

第一条,任何批量操作之前,先复制三个文件到测试目录跑一遍脚本。不要直接在大目录上跑。我第一次写这个脚本时,直接对着两百多个文件执行,结果发现还要微调清理正则,来回折腾了很久。后来学乖了,先复制三五个典型文件到临时目录,跑通了再去处理真实目录。

第二条,脚本里永远不要写删除文件的逻辑。哪怕你确定某个文件重复了,也不要在脚本里顺手删掉。重命名的可逆性比删除高得多,出问题还能按日志恢复,删除基本就没了。如果确实需要清理,等重命名确认无误后,再人工去处理。

第三条,运行完脚本后,随便打开几个文件确认一下播放器显示是否正常,而不是只看文件名列表。我遇到过一种情况:文件名看着对,但文件关联的播放器数据库缓存还是旧的名字,导致播放列表显示错乱。确认“显示层”正常,才算是真正完成了这次整理工作。

经验收尾:这个脚本思路还能用在哪

写到这里,方法论其实已经讲透了。最后分享一点个人感受:批量重命名MP4文件这件事,只是一个很小的技术切片,但它背后的“扫描输入格式 → 设计提取规则 → 先试跑后执行 → 记录操作日志”这个套路,几乎可以平移到我日常的很多素材整理工作中。比如处理图片时去掉相机拍摄前缀、整理电子书文件名里的多余标点、批量规整字幕文件与视频文件的对齐命名,都是同一个逻辑框架。区别只在于正则和扩展名变了,PowerShell脚本的骨架几乎不用动。

我在实际工作中最常用到的一个小技巧是:把脚本里的目录参数化之后,在文件管理器地址栏直接输入powershell -File Clean-MusicFileNames.ps1 -Directory "某个路径",配合-WhatIf参数先预览,不影响任何文件。这样长期用下来,整理素材库的效率比手动拖拽、右键重命名高出一个数量级。

如果后续还想升级这个脚本,我觉得有两个方向值得尝试:一是把“未能匹配的文件”自动生成一个HTML报告,里面包含文件名和推荐命名建议,这样处理特殊文件名时可以边看边改;二是加上重复文件检测,在重命名前先提示“同一歌手同一歌曲可能有多个版本”,让你决定是否保留全部版本。这两个方向都需要额外的逻辑和测试,但对于长期维护大素材库的人来说,投入产出比是划算的。你可以先跑通今天这个版本,再按自己的实际需求往这两个方向扩展。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询