Powershell使用regex匹配组重命名

hjzp0vay  于 11个月前  发布在  Shell
关注(0)|答案(4)|浏览(117)

我正在努力编写一个Powershell命令,它可以执行以下操作。假设一个文件夹中有一堆文件,这些文件的随机名称与正则表达式模式匹配。我想捕获与模式匹配的部分,并将文件重命名为该部分。
例如,如果模式为\w\d+\w+\d+(或类似模式),则“asdjlk-c12aa13-.pdf”应变为“c12aa13.pdf”。
我目前的想法是这样的:
Get-ChildItem | Rename-Item -NewName { $_.Name -match $pattern ... } -WhatIf
其中...需要替换为将代码块的“值”(即NewName)设置为匹配组的内容。即,我不知道如何在-match命令后直接访问$matched
另外,我想知道是否可以使用-match进行延迟匹配,.*?似乎做不到这一点。

k97glaaz

k97glaaz1#

tl;dr
使用-replace而不是-match在 * 单个 * 操作匹配 * 和 * 提取感兴趣的部分,这需要您:

  • 设计你的regex以匹配 * 整个 * 输入字符串,
  • 将与(…)中感兴趣的部分匹配的子表达式括起来,即 capture groups
  • 并引用替换操作数中的这些部分; $1引用 * 第一个 * 捕获组捕获的内容,$2引用第二个捕获组捕获的内容,依此类推。
Get-ChildItem |
  Rename-Item -NewName { $_.Name -replace '^.*\b(\w\d+\w+\d+)\b.*(\.pdf)$', '$1$2' } -WhatIf

字符串
注意事项:与您自己的代码一样,上述命令中的**-WhatIf公共参数*预览 * 操作**。删除-WhatIf,并在确定操作将按您的要求执行后重新执行。
请注意,不匹配正则表达式的输入文件将保持不变。
详情请继续阅读。
至于:
我想知道是否有可能使用-match进行延迟匹配,.*?似乎不起作用。
上面使用\b(字边界Assert)作为延迟匹配的 * 更健壮 * 的替代方案,但.*?原则上 * 确实 * 工作,如以下简化示例所示:

# -> 'c12aa13.pdf'
'asdjlk-c12aa13-.pdf' -replace '^.*?(\w\d+\w+\d+).*(\.pdf)$', '$1$2'


也就是说,.*之后的?确保了c匹配被“放弃”,以便尽可能早地匹配下面的子表达式(\w\d+\w+\d+)-转到this regex101.com page并尝试删除?以查看行为上的差异。

-replace技术和正则表达式解释:

虽然您可以在自己的尝试中遵循-match操作,随后通过自动$Matches变量提取匹配的零件,但在-replace操作符的帮助下,通常更容易 * 合并 * 这两个操作:
你只需要确保为了只返回你感兴趣的部分,你必须匹配输入字符串 * 完整 *,然后忽略你不关心的部分,如下面这个简化的例子所示:

# -> 'c12aa13.pdf'
'asdjlk-c12aa13-.pdf' -replace '^.*\b(\w\d+\w+\d+)\b.*(\.pdf)$', '$1$2'


有关正则表达式的更详细解释和使用它的选项,请参阅this regex101.com page

  • .*\b匹配感兴趣部分之前的前缀; \b确保以下子表达式仅在 * 单词边界 * 处匹配(即仅在字母数字1或_之外的字符 * 处匹配)。
  • (\w\d+\w+\d+)匹配感兴趣的部分, Package 在捕获组中;由于它是正则表达式中的第 * 1 * 个捕获组,因此可以在替换操作数中将其捕获的内容引用为$1
  • \b.*,在一个字的边界,匹配的一切之后,直到.pdf文件扩展名。
  • (\.pdf)$与名称末尾的文件扩展名.pdf匹配,作为 2nd 捕获组,可以在替换操作数中引用为$2
  • 请注意,对包含扩展名的完整.Name值进行操作的另一种方法是仅匹配.BaseName属性,并在之后附加.Extension属性,沿着行:
($_.BaseName -replace '…','…') + $_.Extension

  • $1$2简单地连接2个capture-group匹配以输出所需的名称。
  • 注意事项:一般来说,对正则表达式和替换操作数都使用 * 单引号 * 字符串,这样$就不会被 PowerShell 事先意外解释。
  • 有关-replace和替换操作数语法的详细信息,请参见this answer
kmynzznz

kmynzznz2#

一个更安全的方法是使用一个测试(类似于-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; }

字符串
这是安全得多,因为重命名可以是灾难性的,当运行错误。

anauzrmj

anauzrmj3#

你可以在脚本块中放尽可能多的东西。同时隐藏-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".

字符串

vawmfj5a

vawmfj5a4#

老实说,我不确定你上面的行是否有效。如果“\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循环”(%{...})以重命名每个对象。

相关问题