使用 Powershell 将第二次出现的“-”替换为“_”

Replacing 2nd occurrence of "-" with "_" using Powershell

我一直在使用 Powershell 来简化创建目录、重命名和移动文件等重复性任务。我正在处理视频和 PDF 文件,其中所需的文件名语法非常具体。到目前为止,我已经能够纠正我遇到的所有常见错误 运行,但是这个错误让我很困惑。

我的文件的正确语法包括:

01A-50_02A-50-CIPP-PRE.MP4
01AA-50_02AA-50-CIPP-PNSL.PDF
W01AA-48_02AA-48-CIPP-PST-CMP.MPG

我收到了大量如下所示的文件:

01A-50-02A-50-CIPP-PRE.MP4
01AA-50-02AA-50-CIPP-PNSL.PDF
W01AA-48-02AA-48-CIPP-PST-CMP.MPG

我需要用下划线替换第二个破折号,同时保持其他破折号不受影响。否则我可以在 excel 的帮助下批量执行此操作,但我希望有一个简短的代码可以找到并更正此语法错误,而无需将列表导出到 excel,使用文本到列,然后将字母数字部分连接在一起。我也不想手动更正所有这些文件名。

根据我的研究,不可能针对特定字符的出现进行替换。我最接近的想法是我找到了一个涉及 REGEX 以及识别和替换模式的解决方案。我没能对此做任何有建设性的事情。

我使用此代码的方式是打开包含命名错误文件的文件夹,在那里打开 Powershell window,从我桌面上的 txt 文件复制代码,然后将其粘贴到 Powershell .

如有任何帮助,我们将不胜感激。

查看您的示例,似乎第二个 - 总是出现在数字之间。类似于 $Variable -replace 'REGEX','_'

使用下面的正则表达式将匹配这些。

(?<=[0-9])(.)(?=[0-9])

()创建一个组来匹配,它是一个捕获组。

?<= 是正向后视,它匹配主表达式之前的组,但不将其包含在结果中

[0-9]是字符集,匹配0到9之间的任何字符。

. 匹配除换行符以外的任何字符

?= 是一个积极的向前看,它匹配主表达式之后的一个组,而不将其包含在结果中

我建议使用 Regexr 来测试和学习正则表达式。

这个 RegEx 怎么样:(?<=(^|\n)[^-]*-[^-]*)-

或作为完整命令(使用 Replace Part of File Name Powershell 的答案):

Get-ChildItem | Rename-Item -NewName {$_.name -replace '(?<=^[^-_]+-[^-_]+)-','_'}

编辑:incorporated suggestions from @mklement0

您可以在 - 的前两次出现处拆分字符串,然后通过 -_:

将它们连接起来
$name = '01A-50-02A-50-CIPP-PRE.MP4'
$first,$second,$rest = $name -split '-',3
$newName = "${first}-${second}_${rest}"

在正则表达式中使用 -replace 运算符:

Get-ChildItem |
  Rename-Item -NewName { $_.Name -replace '^([^_-]+-[^_-]+)-', '_' } -WhatIf

-WhatIf 预览重命名操作;删除它以执行实际重命名。

  • 正则表达式 '^([^_-]+-[^_-]+)-' 使用捕获组 ((...)) 捕获除第二个 -.

    之外的标记
    • [^_-]+ 捕获既不是 - 也不是 _ 的任何非空 运行 字符。 _ 也被排除,以防止文件名已经 正确的误报;对于那些,不排除 _ 将匹配第一个 3 标记并在那里插入一个 additional _
  • 替换操作数 _ 然后使用第一个(也是唯一一个)捕获组 (</code>) 的值,后跟文字 <code>_ 来替换正则表达式匹配,有效地将第二个 - 替换为 _.

  • 如果给定的文件名与正则表达式不匹配(如果它已经是正确的),则该名称按原样返回,这在 [= 的上下文中是一个安静的空操作29=].

谢谢 Solomon Ucko! 这几乎正​​是我要找的。

获取子项 |重命名项目 -NewName {$.name -replace '(?<=(^|\n)[^-]*-[^-]*)-',''}

它在我可以抛出的所有示例上都非常有效,除了... 如果我 运行 将代码放在错误命名和正确命名文件的混合组上,它会在不属于它的地方添加另一个下划线...

"E21U-50A_E21U_50-CIPP-PST-CMP"

而不是

"E21U-50A_E21U-50-CIPP-PST-CMP"

解决这个问题很简单。
我所做的只是首先将所有 _ 替换为 -

Get-ChildItem | Rename-Item -NewName {$_.name -replace '_','-'}

Get-ChildItem | Rename-Item -NewName {$_.name -replace '(?<=(^|\n)[^-]*-[^-]*)-','_'}

感谢所有有其他想法的人。诚然,我还没有尝试过它们,因为这个解决方案是我尝试的第一个解决方案,并且成功了。
但是,我将在完成工作后修改其他解决方案。
再次感谢。