如何在Powershell中使用-replace进行全局替换?

b09cbbtk  于 2023-02-04  发布在  Shell
关注(0)|答案(2)|浏览(204)

我是Powershell的新手,所以请理解。
我有这样的模式
(.*?)(\d{3})(.*?:\r?\n)(?!\2)(\d{3})
匹配此文本:

111 is different from:
111 is different from:
123 is different from:
567.

这只给出了1个匹配,而那里有2个示例。这怎么可能实现呢?模式在第一个示例中消耗了123,所以找不到它。我不得不重复这行几次来克服这个问题。我相信还有其他的方法。请帮助。
尝试将123模式更改为lookahead。但无法捕获123
目标:我想在两个不同的值之间插入一行,一个句子。
编辑:像这样

111 is different from:
111 is different from:
   *** This value 123 ***
123 is different from:
  *** This value 567 ***
567.
yruzcnhs

yruzcnhs1#

您可以使用2个捕获组,其中第一个组用于负前瞻,第二个组用于在替换后获得正确的结果。

^(\d{3})\b.*:(?=\r?\n(?!\1)(\d{3})\b)

在替换中使用完全匹配和组2:

$0\n   *** This value $2 ***

参见.NET regex101 demo
产出

111 is different from:
111 is different from:
   *** This value 123 ***
123 is different from:
   *** This value 567 ***
567.

如果你想让 position 位于字符串的开头,Assert下一行不是以第一行开头的数字开头,那么整个模式将处于正的前瞻Assert中:

^(?=(\d{3}\b)(.*:\r?\n)(?!\1)(\d{3})\b)

参见另一个.NET regex101 demo

xbp102n0

xbp102n02#

注意,PowerShell的-replace操作符是 * 总是 * 全局的,即它 * 总是 * 查找并替换给定正则表达式的 * 所有 * 匹配项。
请改用以下-replace操作:

@'
111 is different from:
111 is different from:
123 is different from:
567.
'@ -replace '(?m)^(\d{3}) .+:(\r?\n)(?!\1)(?=(\d{3})\b)', 
            '$0  *** This value $3 ***$2'

注意:用于多行输入文本的@'<newline>...<newline>'@字符串字面量是所谓的here-string。
输出:

111 is different from:
111 is different from:
  *** This value 123 ***
123 is different from:
  *** This value 567 ***
567.
  • 有关正则表达式的详细解释以及使用它进行实验的能力,请参见this regex101.com page,但简而言之:
  • (?m)Multiline. NET regex选项的内联形式,它使^$在 * 每行 * 的开头和结尾匹配。
  • 因此,^(\d{3})仅在捕捉组中的行开始处匹配3位数序列,而.+:匹配同一行上的空格和至少一个附加字符,直到末尾处的:
  • (\r?\n)捕获在第二个捕获组中遇到的特定换行符序列(可能是CRLF(Windows格式)或仅是LF(Unix))。
  • 捕获特定的换行符序列允许您通过占位符$2在替换字符串中 * 复制 * 它,以确保新插入的行以 * 相同的序列 * 结束。
  • 如果您不关心在生成的字符串中可能混合使用\r\n\n,则可以省略第二个捕获组并使用"n"(原文如此)或"rn",使用一个 * expandable * 字符串("...")与[escape sequence](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Special_Characters)-请注意,与C#不同的是,在PowerShell字符串文本中,\r\n* 无法 * 识别(只有. NET * regex引擎 * 能够识别它们,但在-replace的 * 替换操作数 * 中 * 不能 * 识别它们,-replace* 不是 * 正则表达式,并且其中只有$`前缀的占位符能够被识别)。
# Conceptually cleaner: separate the verbatim part from
# the expandable part.
('$0  *** This value $2 ***' + "`n")

# Alternative, using a single "..." string
# The '$' chars. that are part of -replace *placeholders*
# must be *escaped as `$* to prevent up-front expansion by PowerShell
"`$0  *** This value `$2 ***`n"
  • (?!\1)(?=(\d{3})\b)使用负((?!...))和正(?=...)前瞻Assert * 两者 * 来查找下一行开始处(由于\b,在字边界处)的与当前行上的3个数字 * 不 * 相同的3个数字(\1是对第一捕获群组所匹配的数字的 * 反向参考 *)。
  • 注意,在一个整体的按定义的非捕获lookaroundAssert中使用捕获组是可能的,并且实际上在上面被用来捕获在后续行开始处的3位数序列,通过替换字符串中的占位符$3引用。
  • 在替换字符串中,$0$2$3分别表示 * whole * regex、第二个捕获组和第三个捕获组捕获的内容(可以使用$&代替$0;有关这些占位符的更多信息,请参见this answer)。
  • 请注意,通过使用 * string * 作为替换操作数,您只能通过占位符(如$0)(有关这些占位符的详细信息,请参阅this answer)嵌入捕获的文本 * as-is *。如果您需要 * 完全动态地 * 确定替换文本,即如果它需要基于每个匹配应用 * transformation *:
  • 在PowerShell(Core)7+中,您可以改用脚本块{ ... }
  • 在Windows PowerShell中,您必须直接调用底层的[regex]::Replace()方法。
  • 见下文。

要详细说明完全动态替换方法,请在本例中将1添加到捕获的数字:
PowerShell (Core) 7+解决方案,使用脚本块({ ... })作为-replace的替换操作数:

@'
111 is different from:
111 is different from:
123 is different from:
567.
'@ -replace '(?m)^(\d{3}) .+:(\r?\n)(?!\1)(?=(\d{3})\b)', {
               '{0}  *** This value + 1: {1} ***{2}' -f $_.Value, ([int] $_.Groups[3].Value + 1), $_.Groups[2].Value
            }
$str = @'
111 is different from:
111 is different from:
123 is different from:
567.
'@

[regex]::Replace(
  $str, 
  '(?m)^(\d{3}) .+:(\r?\n)(?!\1)(?=(\d{3})\b)', 
  {
    param($m)
    '{0}  *** This value + 1: {1} ***{2}' -f $m.Value, ([int] $m.Groups[3].Value + 1), $m.Groups[2].Value
  }
)

输出(请注意,已将1添加到每个捕获值):

111 is different from:
111 is different from:
  *** This value + 1: 124 ***
123 is different from:
  *** This value + 1: 568 ***
567.

相关问题