【问题标题】:Regex replace multilines in powershell正则表达式替换powershell中的多行
【发布时间】:2018-10-15 13:33:39
【问题描述】:

我想在每行末尾用 Windows CRLF 替换以 UTF-8 编码的 AssemblyInfo.cs 中的这些行

<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release

通过这些

[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]

为此,我有一个 powershell 脚本,它将解析我的所有文件并进行替换。

我在 regex101 中准备的正则表达式是 this one 并且适用于 101:

<<<<<<<\sHEAD\n\[assembly:\sAssemblyVersion\("2\.0\.0\.0"\)\]\n\[assembly:\sAssemblyFileVersion\("2\.0\.0\.0"\)\]\n=======\n\[assembly:\sAssemblyVersion\("1\.1\.0\.0"\)\]\n\[assembly: AssemblyFileVersion\("1\.1\.0\.0"\)\]\n>>>>>>>\sv1_final_release

我无法使 -replace 在新行上工作。 但是当仅定位&lt;&lt;&lt;&lt;&lt;&lt;&lt;\sHEAD 时,它会匹配并执行替换。

以下所有变体均失败:

  • &lt;&lt;&lt;&lt;&lt;&lt;&lt;\sHEAD\n\[assembly:没有错误没有替换
  • &lt;&lt;&lt;&lt;&lt;&lt;&lt;\sHEAD\r\n\[assembly:没有错误没有替换
  • &lt;&lt;&lt;&lt;&lt;&lt;&lt;\sHEADrn\[assembly: 没有错误没有替换,write-host 将其打印为 <<<<<<<\sHEAD \[assembly:

这与/gm(*CRLF) 无关

我的 powershell 信息说明:

$ConflictVersionRegex = "<<<<<<<\sHEAD\n\[assembly:\sAssemblyVersion\(`"2\.0\.0\.0`"\)\]\n\[assembly:\sAssemblyFileVersion\(`"2\.0\.0\.0`"\)\]\n=======\n\[assembly:\sAssemblyVersion\(`"1\.1\.0\.0`"\)\]\n\[assembly: AssemblyFileVersion\(`"1\.1\.0\.0`"\)\]\n>>>>>>>\sv1_final_release" 
$ConflictVersionRegexTest = "<<<<<<<\sHEAD`r`n\[assembly:" 
$fileContent = Get-Content($filePath)   
$filecontent = $filecontent -replace $ConflictVersionRegexTest, $AssemblyNewVersion
[System.IO.File]::WriteAllLines($filePath, $fileContent, $Utf8NoBomEncoding)

我错过了什么?为什么不替换?

非常感谢

【问题讨论】:

  • 为什么使用正则表达式而不是简单的替换? .Net 也使用自己的小正则表达式。似乎你在 PCRE 中制作了你的正则表达式

标签: regex powershell newline


【解决方案1】:

根据 Poutrathor(OP)的反馈,存在两个问题:

  • 主要问题Get-Content($filePath)(应该写成
    Get-Content $filePath[1]逐行读取文件,当在变量中捕获时,这会产生一个行数组
    -replace 然后运行在每个输入行单独,这意味着跨行正则表达式不会匹配任何内容。

    • 解决方案:使用Get-Content -Raw (PSv3+) 将文件作为一个整体读入一个单行的多行字符串。
  • 其次,您提到需要将 regex 换行符(行尾)转义序列 (\n) (LF) 替换为其 PowerShell 字符串插值对应项 (`n) - 请注意,PowerShell 使用 `,即 反引号 作为转义字符:

    • 请注意,这仅在 替换 字符串中是必需的,以便在输出上创建实际的文字换行符(换行符) em> - 而不是使用正则表达式构造 \n匹配换行符。

    • 但是,在 Windows 上,换行符通常是 CRLF 序列,即,一个 CR(\r`r)紧跟一个 LF(\n / `n) - 即 \r\n/ `r`n - 而在类 Unix 平台上,它们只是 LF,\n / @987654338 @

      • 如果您不确定给定输入具有哪种样式的换行符,使用\r?\n 以跨平台兼容的方式匹配换行符
        如果您不关心输入有哪些特定的换行符,则可以按照习惯有条不紊地使用它。
    • 因此:

      • 在您的 regex 中,您可以在 \r\n`r`n 之间选择,请注意:

        • `r`n 仅适用于 双引号 "..." 字符串。
        • 通常最好使用文字,单引号字符串来存储正则表达式 - 需要使用\r\n (Windows) / \n (Unix) /\r?\n(与平台无关) - 这样就不会混淆 PowerShell 预先插入字符串的哪些部分与正则表达式引擎解释哪些部分。
      • 在您的替换字符串中,在"..." 中使用`r`n 来创建实际的换行符。


作为使用转义序列的替代方法来表示换行符,您可以使用here-strings 方便地定义具有实际换行符(换行符)的多行字符串,如显示在Paweł Dyl's answer,但有一个警告

  • here-strings 总是与封闭的脚本文件具有相同的换行符样式,这意味着:
    • 仅当输入恰好与 脚本文件具有相同样式的换行符时,基于此处字符串的正则表达式才会匹配。
    • 基于 here-string 的替换字符串将始终使用脚本文件的换行符样式。

[1] 您的调用看起来像一个 .NET method 调用,虽然它恰好在这种情况下有效,但应避免此类语法混淆:PowerShell cmdlet 和函数的调用方式类似于 shell 命令:不带括号 ((...)) 并带有 空格 分隔的参数。

【讨论】:

  • 那么社区答案是如何产生的? :0 非常感谢您的深入解释。 Powershell 很棘手。
  • 我很高兴,@Poutrathor。 PowerShell 是……呃……功能强大,所以它肯定会变得很棘手。这里的问题之一是两个不相关的世界混合在一起:PowerShell 的字符串插值和 (.NET) 正则表达式。您所说的社区答案是什么意思?
  • 我明白了 - 谢谢,@Poutrathor。如果您不只是在开玩笑:我认为这个特定的答案不是一个好的候选者。
【解决方案2】:

请看以下演示:

$newText = @'
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
'@

$src = @'
<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release
Other lines and second instance
<<<<<<< HEAD
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")]
=======
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
>>>>>>> v1_final_release
Some other lines
'@

$src -replace ('<<<<<<< HEAD\s+',
    '\[assembly: AssemblyVersion\("2\.0\.0\.0"\)\]\s+',
    '\[assembly: AssemblyFileVersion\("2\.0\.0\.0"\)\]\s+'+
    '=======\s+'+
    '\[assembly: AssemblyVersion\("1\.1\.0\.0"\)\]\s+',
    '\[assembly: AssemblyFileVersion\("1\.1\.0\.0"\)\]\s+'+
    '>>>>>>> v1_final_release'),$newText

另外,请确保您的内容被读取为一个大字符串。这可以使用Get-Content $path -Raw[System.IO.File]::ReadAllText($path) 来实现。

【讨论】:

  • 这里带有实际换行符的字符串绝对方便,但在正则表达式中,需要注意的是它们总是使用封闭脚本文件的换行符(行尾)样式,这可能与输入的匹配也可能不匹配.
猜你喜欢
  • 2012-06-20
  • 2016-02-13
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-13
相关资源
最近更新 更多