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

时间:2018-11-15 22:31:25

标签: regex powershell replace renaming

我一直在使用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窗口,从桌面上的txt文件复制代码,然后将其粘贴到Powershell中。

对此将提供任何帮助。

5 个答案:

答案 0 :(得分:4)

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

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

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

  • 正则表达式'^([^_-]+-[^_-]+)-'在文件名的开头(-)处捕获前两个^分隔的令牌,并使用捕获组((...))进行捕获除第二个-之外的令牌。

    • [^_-]+捕获既不是-也不是_的所有非空字符。还排除了_,以防止误以为正确的文件名。对于那些,不排除_会匹配第一个 3 令牌,并在其中插入其他 _
  • 然后,
  • 替换操作数$1_使用第一个(也是唯一的)捕获组($1)的值后跟文字_来替换正则表达式匹配的内容,即有效地将第二个-替换为_

  • 如果给定文件名与正则表达式不匹配(如果它已经正确),则该名称按原样返回,这在Rename-Item上下文中是一个安静的no-op。

答案 1 :(得分:1)

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

使用以下正则表达式将与之匹配。

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

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

?<=是正向查找,它与主表达式之前的组匹配,但不包含在结果中

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

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

?=是一个正向预测,它与主表达式后的一组匹配,而不包含在结果中

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

答案 2 :(得分:1)

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

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

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

编辑:incorporated suggestions from @mklement0

答案 3 :(得分:1)

您可以在前两个-处拆分字符串,然后通过-_将它们连接起来:

$name = '01A-50-02A-50-CIPP-PRE.MP4'
$first,$second,$rest = $name -split '-',3
$newName = "${first}-${second}_${rest}"

答案 4 :(得分:1)

谢谢所罗门·乌科! 那几乎就是我想要的。

Get-ChildItem |重命名项-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)[^-]*-[^-]*)-','_'}

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