Select-String 对输入的 数组 进行操作,因此 您必须提供一个 而不是 单个多行字符串 >行数组让-Context和-AllMatches按预期工作:
$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@
$teststring -split '\r?\n' | Select-String -Pattern "line3" -AllMatches -Context 1,0 | % {
"line before: " + $_.Context.PreContext[0]
"matched part: " + $_.Matches.Value # Prints the what the pattern matched
}
这会产生:
line before: line2
matched part: line3
line before: line5
matched part: line3
Select-String 很方便,但慢。
特别是对于已经在内存中的单个字符串,使用 .NET Framework 的 [Regex]::Matches() 方法的解决方案会执行得更好,尽管它更复杂。
请注意,PowerShell 自己的 -match 和 -replace 运算符构建在同一个 .NET 类上,但并未公开其所有功能; -match - 在自动 $Matches 变量中报告捕获组 - 这里不是一个选项,因为它只返回 1 匹配项。
以下方法与mjolinor's answer 回答中的方法基本相同,但修正了几个问题[1]。
# Note: The sample string is defined so that it contains LF-only (\n)
# line breaks, merely to simplify the regex below for illustration.
# If your script file use LF-only line breaks, the
# `-replace '\r?\n', "`n" call isn't needed.
$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@ -replace '\r?\n', "`n"
[Regex]::Matches($teststring, '(?:^|(.*)\n).*(line3)') | ForEach-Object {
"line before: " + $_.Groups[1].Value
"matched part: " + $_.Groups[2].Value
}
-
Regex (?:^|(.*)\n).*(line3) 使用 2 个捕获组 ((...)) 来捕获要匹配的行的(匹配部分)和之前的行((?:...) 是一个辅助非-捕获优先级所需的组):
-
(?:^|(.*)\n) 匹配字符串的开头 (^) 或 (|) 任何 - 可能为空 - 非换行符 (.*) 后跟换行符 (\n) 的序列;这确保了当前行没有 时也能找到要匹配的行(即,要匹配的行是 first 行)。
-
(line3) 是定义要匹配的行的组;它前面有.* 以匹配问题中的行为,其中模式line3 被发现,即使它只是一行的part。
- 如果您只想匹配 完整 行,请改用以下正则表达式:
(?:^|(.*)\n)(line3)(?:\n|$)
[Regex]::Matches() 查找 所有 匹配项并将它们作为System.Text.RegularExpressions.Match 对象的集合返回,然后ForEach-Object cmdlet 调用可以对其进行操作以提取捕获组匹配项( $_.Groups[<n>].Value)。
[1] 撰写本文时:
- 没有必要匹配 两次 - 封闭的 if ($teststring -match $pattern) { ... } 是不必要的。
- 不需要内联选项 (?m),因为 . 不 匹配换行符默认情况下。
- (.+?) 仅捕获 非空行(并且不需要非贪婪量词 ?)。
- 如果感兴趣的行是 first 行 - 即,如果没有行 before,则不会匹配。