为什么 powershell -replace 运算符无法正确包含捕获组中捕获的所有字符?

Jos*_*ust 5 powershell

编辑:

再次审查这个问题后,我添加了第二个示例,我希望它能突出显示我的困惑被放大的地方,即有一个通过其索引 (1) 访问的捕获组,而我期望的值碰巧也是$filecontent1 。


此问题表明,在引用其他变量时,可以使用反引号来寻址双引号字符串中的捕获组。

如果您需要在替换表达式中引用其他变量(您可能会这样做),您可以使用双引号字符串并使用反引号转义捕获美元

然而,我看到了一些我无法解释的有趣行为。

$VersionReplacementRegex = "(\d+\.)\d+" #capture first digit + dot b/c I want to keep it
$BuildVersionValidationRegex = "\d+\.\d+\.\d+"
    
$VersionData = [regex]::matches("some-18.11.8",$BuildVersionValidationRegex)
$NewVersion = $VersionData[0] #matches 18.11.8

$filecontent = "stuff 1.0.0.0 other stuff" #Get-Content($file)
Run Code Online (Sandbox Code Playgroud)

$filecontent使用链接问题中指定的捕获组替换文本会产生不完整的结果...

$filecontent -replace $VersionReplacementRegex, "`$1$NewVersion" | Write-Host
Run Code Online (Sandbox Code Playgroud)

回报:118.11.8 预期:1.18.11.8

$1但是在和之间添加空格$NewVersion会产生不同但同样无益的结果。

$filecontent -replace $VersionReplacementRegex, "`$1 $NewVersion" | Write-Host
Run Code Online (Sandbox Code Playgroud)

返回:1. 18.11.8 捕获的点出现在这里,但不需要的空间也出现。

对于此示例,结果有些相似,但捕获组似乎得到了错误的值。

$NewVersion = 18.11.8
$filecontent = "stuff 5.0.0.0 other stuff"
$filecontent -replace "(\d+\.)\d+", "`$1$NewVersion" | Write-Host

# returns: 118.11.8
# expected: 5.18.11.8
Run Code Online (Sandbox Code Playgroud)

在替换字符串中添加空格将返回:5. 18.11.8

那么,我错过了什么,或者有更好的方法吗?

mkl*_*nt0 2

从过去的经验来看,在对该问题的评论中提供关键线索的PetSerAl不会回来发表答案。

太长了;博士

如果您使用-replace引用捕获组 PowerShell变量的替换操作数,请使用诸如 的语法"`${<ndx>}${<PsVar>}",其中<ndx>是捕获组的索引,<PsVar>是 PowerShell 变量的名称;请注意第一个`之前的内容$

PS> $var = '2'; 'foo' -replace '(f)', "[`${1}$var]"
[f2]oo # OK, -replace saw '${1}2'
Run Code Online (Sandbox Code Playgroud)

如果您忽略使用{...}来消除捕获组索引的歧义,则替换会发生故障,因为插值字符串值随后会有效地引用不同的索引:
-replacethen see [$12],由于引用了带有索引 的不存在的捕获组12,因此保持原样:

PS> $var = '2'; 'foo' -replace '(f)', "[`$1$var]"
[$12]oo # !! -replace saw '$12', i.e., a nonexistent group with index 12
Run Code Online (Sandbox Code Playgroud)

将 PowerShell 的字符串扩展
-replace(插值)与运算符的语法混合起来很棘手
,因为很容易混淆

  • 双引号( "...") 字符串中,PowerShell 的通用字符串扩展(字符串插值)功能首先解释$字符,其中前缀引用 (PowerShell)变量以及内部的整个语句$$(...)

  • 无论该扩展的结果是什么字符串,都会由运算符进行解释-replace,其中$- 前缀标记指的是正则表达式匹配操作的结果,如本答案中所总结的。

  • 请注意,这些解释层$完全无关,并且两者都使用印记这一事实$是偶然的。

所以:

  • 如果您的替换操作数不需要字符串扩展,即不需要引用 PowerShell变量表达式,请务必使用单引号字符串 ( '...'),这样 PowerShell 的字符串扩展就不会发挥作用:

     PS> 'foo' -replace '(f)', '[$1]'
     [f]oo  # OK - if you had used "[$1]" instead, the output would be '[]oo',
            # because $1 is then interpreted as a *PowerShell variable*.
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您确实需要涉及字符串扩展

    • 前缀$字符。应该传递-replace `

      • `(反引号)是 PowerShell 的通用转义字符,在"..."字符串中它用于指示下一个字符要按字面意思处理;放在 a 之前$,它会抑制该标记的字符串插值;例如,"I'm `$HOME"产生字面量I'm $HOME,即变量引用没有扩展
    • 为了消除对捕获组的引用的歧义,例如$1,,将它们括在{...}- 例如,${1}

      • 请注意,您可能还需要使用{...}来消除PowerShell变量名称的歧义;例如"$HOME1"必须是"${HOME}1"为了引用变量$HOME成功引用变量。
      • 此外,它不仅仅涉及捕获组索引;还涉及捕获组索引命名捕获组也可能出现歧义;"..."基于替换的操作数中,始终使用{...}捕获组索引/名称(和 PS 变量)是一个好习惯。
    • 如果有疑问,请单独输出替换操作数以检查-replace最终会看到什么。

      • 在上面的示例中,"[`$1$var]"应用字符串插值步骤的自身输出会使问题更加明显:[$12]

为了说明后一点:

PS> $var = '2'; 'foo' -replace '(f)', "[`$1$var]"
[$12]oo  # !! $1 wasn't recognizes as the 1st capture group.
Run Code Online (Sandbox Code Playgroud)

问题是-replace,在字符串扩展之后,将 视为[$12]替换操作数,并且由于没有带有索引的捕获组12,因此它保持原样。

将捕获组编号括起来可以{...}解决问题:

PS> $var = '2'; 'foo' -replace '(f)', "[`${1}$var]"
[f2]oo  # OK
Run Code Online (Sandbox Code Playgroud)