使用正则表达式匹配组进行PowerShell重命名

5
我很难编写一个Powershell命令来完成以下操作。假设有一个文件夹,其中有许多文件具有与正则表达式模式匹配的随机名称。我想捕获与该模式匹配的部分,并仅将文件重命名为该部分。
例如,如果模式是\w\d+\w+\d+(或类似),"asdjlk-c12aa13-.pdf"应更改为"c12aa13.pdf"。
我的当前想法大致如下:
Get-ChildItem | Rename-Item -NewName { $_.Name -match $pattern ... } -WhatIf
其中...需要替换为设置代码块(即NewName)的“值”的内容。也就是说,我不知道如何在-match命令之后直接访问$matched。
此外,我想知道是否可能使用-match进行懒惰匹配,.*?似乎行不通。

1
对于正则表达式,您可以在组中捕获所需内容,并在替换中使用这些组。示例 - The fourth bird
正是我所想的,是的。PowerShell 更具问题 :/ - Xaser
呵呵...符合正则表达式的随机名称。我不确定你知道随机是什么意思。 :) - EBGreen
@EBGreen 可能选择词汇不太恰当,但确实存在随机部分的意思是文件名中有一个随机部分不受模式控制,而且模式可以是任何东西,构成模式的符号也可以是随机的。 - Xaser
4个回答

4

你可以使用-match操作匹配字符串,然后通过自动变量$Matches提取匹配部分,但通常用-replace操作符将两个操作结合起来更容易:

你只需确保为了只返回感兴趣的部分,必须完全匹配输入字符串,然后忽略你不关心的部分:

PS> 'asdjlk-c12aa13-.pdf' -replace '^.*?(\w\d+\w+\d+).*?(\.pdf)$', '$1$2'
c12aa13.pdf
  • ^.*?(懒惰模式)匹配感兴趣部分之前的前缀。

  • (\w\d+\w+\d+) 匹配被包裹在捕获组中的感兴趣部分;由于它是正则表达式中的第一个捕获组,因此可以在替换操作数中引用$1以表示它所捕获的内容。

  • .*?(懒惰模式)匹配直到.pdf文件名扩展名后的所有内容。

  • (\.pdf)$ 匹配文件名扩展名.pdf并将其捕获,作为第二个捕获组,可以在替换操作数中引用$2

  • $1$2 简单地连接两个捕获组匹配,以输出所需的名称。

    • 注意:通常使用单引号字符串来表示正则表达式和替换操作数,这样$就不会被PowerShell错误解释。

    • 有关-replace及替换操作数语法的更多信息,请参阅我的答案


在您的命令上下文中的解决方案:

Get-ChildItem |
  Rename-Item -NewName { $_.Name -replace '^.*?(\w\d+\w+\d+).*?(\.pdf)$', '$1$2' } -WhatIf

2

更安全的方法是使用类似于-WhatIf的测试。

这个例子将文件从DSC12345 - X-1.jpg重命名为DSC12345-X1.jpg

# first verify what your files will convert too
# - gets files
# - pipes to % (foreach)
# - creates $a variable for replacement
# - echo replacement
Get-ChildItem . | % { $a = $_.name -replace "^DSC(\d+)\s-\s([A-Z])-(\d).jpg$",'DSC$1-$2$3.jpg'; echo "$_.name => $a"; }

# example output:
# DSC04975-W1.jpg.name => DSC04975-W1.jpg
# DSC04976-W2.jpg.name => DSC04976-W2.jpg
# DSC04977-W3.jpg.name => DSC04977-W3.jpg
# ...

# use the same command and replace "echo" with "ren"
Get-ChildItem . | % { $a = $_.name -replace "^DSC(\d+)\s-\s([A-Z])-(\d).jpg$",'DSC$1-$2$3.jpg'; ren $_.name $a; }

如果操作不当,重命名可能会带来灾难性的后果,因此这种方法更加安全。


1
使用-WhatIf是安全的,没有理由重新发明它。将-WhatIf-replace操作结合起来,以预览结果,就像接受的答案中所示,已经足够了。 - undefined
我认为区别在于我更喜欢获得变化的视觉记录。 - undefined
-WhatIf-的设计本身就提供了显示输出,或者您是指希望控制这些-WhatIf-消息的特定格式(而-WhatIf-无法控制)?如果是这样,我建议您相应地回答。"更安全的方法"部分是误导性的。 - undefined
1
附注:为了可视化在执行实际重命名时发生的情况,即不使用“-WhatIf”,您可以使用“-Verbose”(本质上打印与“-WhatIf”相同的消息)。 - undefined

1

在脚本块中可以放置任意数量的内容。同时,使用-match隐藏输出。正则表达式使用“?”实现懒惰匹配。

Get-ChildItem | Rename-Item -NewName { [void]($_.Name -match '.+?'); $matches.0 } -WhatIf

What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/afile Destination: /Users/js/foo/a".
What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/bfile Destination: /Users/js/foo/b".
What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/cfile Destination: /Users/js/foo/c".


0
说实话,我不确定你上面的代码是否有效。如果“\w\d+\w+\d+”是你要查找的模式,我会这样做:
[regex]$regex = "\w\d+\w+\d+"    
Get-ChildItem | ?{$_.name -match $regex} | %{rename-item $_ "$($regex.Matches($_).value).pdf"}

在这种情况下,您将Get-ChildItem的输出管道传送到“foreach where循环”(?{...}),然后将此输出管道传输到“foreach循环”(%{...})以重命名每个对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接