如何在保留输入大小写的同时用 case-insensitive 匹配替换 PowerShell 中的字符串

How to replace strings in PowerShell with case-insensitive matching while preserving the input case

我想替换文件中的多个字符串。如果字符串是大写字母,则用大写字母替换。如果是小写就用小写替换。

到目前为止,我的代码如下所示。它确实会替换,但不会按大小写替换。

(Get-Content $xyx -ErrorAction Stop) | Foreach-Object {
  $_ -replace 'abc001', 'abc002' -replace 'cde001', 'cde002' 
} | Set-Content $xyz

有时我要替换的字符串是大写的。所以我需要引入一个检查,如果它是大写替换为大写,否则小写。

这段代码怎么写?

编者注:在首次发布此问题时,要求在替换字符串中保留原始大小写的同时匹配 case-insensitively 的要求并不十分明显 - 标题后来被修改,之后部分答案已发布。

-replace 正则表达式运算符默认区分大小写 in。如果您想更改该行为,您需要使用正则表达式修饰符 (-replace '(?-i)abc001', 'abc002') 或更简单的 -creplace 'abc001', 'abc002' 来使操作区分大小写。

但是,由于您在这里似乎没有使用正则表达式,我建议您使用字符串运算符 .Replace(),因为它默认区分大小写。

$_.Replace('abc001', 'abc002').Replace('cde001', 'cde002')

来自另一个问题 here,尝试这样的事情 -

$i = 0
(Get-Content $xyx -ErrorAction Stop) | Foreach-Object {
    $line = $_[$i];
    if ($line -cmatch "^[^a-z]*$")
    {
        $line -replace $Matches.Value, 'YourNewValue'.ToUpper()
    }
    elseif ($line -cmatch "^[^A-Z]*$")
    {
        $line -replace $Matches.Value, 'YourNewValue'.ToLower()
    }
    $i++    
} | Set-Content $xyz

原答案使用正则匹配。原始答案中已经提到了正则表达式的解释。我所做的只是引入一个大写和小写检查并同样替换它。

就像original answer中提到的,诀窍是使用-cmatch运算符。 -cmatch 始终 区分大小写。

来到正则表达式,

  • 字符串开头的anchor(^)
  • 一个 character class 小写拉丁字母 ([a-z])。同样([A-z] 为大写)。
  • A quantifier,告诉要重复字符 class 至少 0 次,从而根据需要匹配尽可能多的字符 (*)。您可以使用 + 来禁止空字符串。
  • anchor为字符串结尾($)。这两个锚点确保正则表达式必须匹配字符串中的 每个 字符。如果您只使用 [a-z]* 那么这将匹配任何至少包含 0 个小写字母 某处 的字符串。这将是每个字符串。

如果您的字符串也可能由字母以外的其他内容组成,并且您想确保其中的每个 字母 都是小写,而不是还要求字符串包含仅包含字母,则必须反转字符 class,类似于:

if ($xyz -cmatch "^[^A-Z]*$") { ... }

字符class开头的^ 反转 class,匹配每个字符not包括在内。因此,只有当字符串在某处包含大写字母时,此正则表达式才会失败。尽管如此,仍然需要-cmatch

听起来您正在寻找的是 匹配 不区分大小写 的替换操作, 但 case-preserving on replacement.

您没有指定要匹配的字符串和用什么替换它们之间的确切关系,而是按照您的示例命令进行:

(Get-Content $xyx -ErrorAction Stop) -replace '(abc00)1', '2' -replace 
                                              '(cde00)1', '2' | Set-Content $xyz

注意:根据手头的特定替换,可以将它们组合成一个操作:
... -replace '(abc00|cde00)1', '2'

  • 鉴于 -regex 默认情况下不区分大小写,它将匹配 abccde 的所有大小写变体,但将它们与 original 第一个(也是唯一一个)捕获组 ((...)) 的情况,可以在替换操作数中引用为 </code>(或者只是 <code>,如果没有歧义)。

  • 另请注意我是如何省略 ForEach-Cmdlet 调用的,因为您可以将 -replace 直接应用于 (Get-Content ...) returns;对于已经在内存中的数组,这种方法不仅更短,而且明显更快。

举个简单的例子:

PS> 'aBc001' -replace '(abc00)1', '2'
aBc002    # input case was preserved