PowerShell中的RegEx行为不可预测,捕获组不限于其范围[重复]

icomxhvb  于 2023-10-22  发布在  Shell
关注(0)|答案(2)|浏览(100)

此问题已在此处有答案

Regex how to replace <'string'> with a new line but keep the 'string'(3个答案)
上个月关门了。
我有一些文本通过$String = Get-Content -Path 'c:\temp\mytext.txt' -Raw导入为单个字符串:

Lorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque
- red
- green
- blue
Lorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque
ac adipiscing mauris ante class placerat per sem quisque phasellus sociosqu, mollis
- red
- green
- blue
bluorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque
`

我想在以-开头的第一行之前("- red"的行)和以-开头的最后一行之后("- blue"的行)添加一个新行,输出应该如下所示:

Lorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque

- red
- green
- blue

Lorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque
ac adipiscing mauris ante class placerat per sem quisque phasellus sociosqu, mollis

- red
- green
- blue

bluorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque

对于以-开头的第一行,根据regex101.com,这个RegEx看起来是它,\n-\s.*(\n)[^-],但是当我尝试将其应用于PowerShell,$String -replace '\n-\s.*(\n)[^-]', '\n$1'时,该行本身被截断,即使捕获组$1由单个令牌\n组成。
同样,对于以-开头的最后一行,根据regex101.com,这个RegEx看起来是\n-\s.*(\n)[^-],但在PowerShell中,$String -replace '\n-\s.*(\n)[^-]', '$1\n'给了我:

Lorem ipsum et cras praesent mollis ullamcorper laoreet mauris imperdiet quisque
ac adipiscing mauris ante class placerat per sem quisque phasellus sociosqu, mollis
- red
\ngreen
...

我的RegEx很弱,我尽力使regex101.com的设置符合PowerShell的设置,但这里有些东西不对劲。
任何帮助将不胜感激!

ar5n3qh5

ar5n3qh51#

tl;dr

替换:
'$1\n'
使用:"$1n",或者-为了概念清晰-使用('$1{0}' -f "n")`

背景信息:

  • regex101.com是一个很好的资源,但是它的.NET支持是以C#为中心的,它不能干净地Map到PowerShell。
  • 请参阅GitHub issue #1838以获得使该站点对PowerShell更加友好的建议。
  • 在PowerShell string literals中,\没有特殊的含义,因此在-replace运算符的替换操作数中包含\n会导致\n被包含。
  • 要在PowerShell中通过 escape sequence 生成 literal LF字符,需要使用可扩展(插值)PowerShell字符串("...")和escape sequence ``n`,使用PowerShell的[escape character](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Special_Characters),即所谓的 backtick
  • 在你的例子中,$1是一个要传递给.NET的正则表达式引擎的占位符,并且考虑到$1看起来也像一个 *PowerShell变量引用 ,在"..."中,你必须 * 转义$ char. 以防止 * 扩展 *(PowerShell基于变量的预先字符串插值),因此上面使用了``$`。
  • 请注意,-replace在后台使用System.Text.RegularExpressions.Regex.Replace(),它的替换(替换)操作数Map到replacement参数,这不是一个正则表达式,它本身不理解基于\的转义序列-它只知道$前缀标记,引用regex匹配的元素(例如$1引用第一个捕获组捕获的内容)。
  • 在C#中,使用"..."字符串(而不是逐字的@"..."字符串),类似"$1\n"的东西会导致 C#\n转义序列 * 预先 * 展开(变成一个字面LF字符)。它会留下$单独,因为这个字符。在C#字符串中没有特殊含义),这是regex101.com在使用.NET (C#)风格时假定的行为。
  • ('$1{0}' -f "n")使用-f(PowerShell的格式操作符),是"$1n"的概念上更清晰的替代方案,因为它使用 * 逐字 * PowerShell字符串文字'...',并带有占位符({0}),它被替换为 *expandable* 字符串文字的值("..."`),明确哪些部分是由前置字符串插值提供的,哪些部分由.NET正则表达式引擎解释。
uplii1fm

uplii1fm2#

我发现同时处理这两种情况更容易。mklement0的答案提供了非常清晰的螺母和螺栓。基本上,这个解决方案从一个破折号开始匹配,直到一个新的行后面没有一个破折号。

$string -replace '(?s)-.+?(?=\n[^-]+)',('{0}$0{0}' -f "`n")

相关问题