PowerShell:匹配前的 Select 行 - Select-String - 使用输入字符串变量时的上下文问题
PowerShell: Select line preceding a match -- Select-String -Context issue when using input string variable
我需要 return 多行字符串变量匹配前的一行。
似乎在对输入使用字符串变量时 Select-String 认为整个字符串都匹配。因此,上下文属性是 "outside" 字符串的任一端并且为空。
考虑以下示例:
$teststring = @"
line1
line2
line3
line4
line5
"@
Write-Host "Line Count:" ($teststring | Measure-Object -Line).Lines #verify PowerShell does regard input as a multi-line string (it does)
Select-String -Pattern "line3" -InputObject $teststring -AllMatches -Context 1,0 | % {
$_.Matches.Value #this prints the exact match
$_.Context #output shows all context properties to be empty
$_.Context.PreContext[0] #this would ideally output first line before the match
$_.Context.PreContext[0] -eq $null #but instead is null
}
我是不是误会了什么?
匹配 "line3" 时 return "line2" 的最佳方法是什么?
谢谢!
编辑:
我忽略说明的其他要求:
需要为不确定长度的字符串提供所有匹配行上方的行。 EG 在下面搜索 "line3" 时,我需要 return "line2" 和 "line5".
line1
line2
line3
line4
line5
line3
line6
您可以使用多行正则表达式,使用 -match
运算符:
$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@
$pattern =
@'
(?m)
(.+?)
line3
'@
if ($teststring -match $pattern)
{ [Regex]::Matches($teststring,$pattern) |
foreach {$_.groups[1].value} }
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
$teststring -split '\r?\n'
将多行字符串拆分成一个行数组:
- 注意:您的此处文档使用的换行符序列(LF-only 与 CRLF)取决于封闭的脚本文件;正则表达式
\r?\n
处理任一样式。
请注意,使用管道提供Select-String
的输入至关重要;如果您使用 -InputObject
,数组将被强制转换回单个字符串。
Select-String
方便,但是慢.
特别是对于已经在内存中的单个字符串,使用 .NET Framework 的 [Regex]::Matches()
方法的解决方案将执行得更好,尽管它更复杂[=116] =].
请注意,PowerShell 自己的 -match
和 -replace
运算符构建在同一个 .NET class 上,但不会公开其所有功能; -match
- 它在自动 $Matches
变量中报告捕获组 - 在这里不是一个选项,因为它只 returns 1 匹配。
以下与 答案中的方法基本相同,但更正了几个问题[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
}
正则表达式 (?:^|(.*)\n).*(line3)
使用 2 个捕获组 ((...)
) 捕获要匹配的行的(匹配部分)和之前的行 ((?:...)
是一个辅助非-捕获组,需要优先级):
(?:^|(.*)\n)
匹配字符串的最开头 (^
) 或 (|
) 任何 - 可能为空 - 非换行符序列 (.*
) 后跟一个换行符 (\n
);这确保了当前面有 no 行时也能找到要匹配的行(即,要匹配的行是 first 行)。
(line3)
是定义要匹配的行的组;它前面有 .*
以匹配问题中的行为,其中找到模式 line3
即使它只是一行的 部分 。
- 如果您只想匹配 完整 行,请改用以下正则表达式:
(?:^|(.*)\n)(line3)(?:\n|$)
[Regex]::Matches()
找到 all 个匹配项,并且 returns 它们作为 System.Text.RegularExpressions.Match
个对象的集合,ForEach-Object
cmdlet 调用然后可以操作以提取捕获组匹配 ($_.Groups[<n>].Value
).
[1] 撰写本文时:
- 不需要匹配 两次 - 封闭的 if ($teststring -match $pattern) { ... }
是不必要的。
- 不需要内联选项 (?m)
,因为 .
默认 不 匹配换行符 。
- (.+?)
仅捕获 非空行 (不需要非贪婪量词 ?
)。
- 如果感兴趣的行是 first 行 - 即,如果没有行 before,则不会匹配。
我需要 return 多行字符串变量匹配前的一行。
似乎在对输入使用字符串变量时 Select-String 认为整个字符串都匹配。因此,上下文属性是 "outside" 字符串的任一端并且为空。
考虑以下示例:
$teststring = @"
line1
line2
line3
line4
line5
"@
Write-Host "Line Count:" ($teststring | Measure-Object -Line).Lines #verify PowerShell does regard input as a multi-line string (it does)
Select-String -Pattern "line3" -InputObject $teststring -AllMatches -Context 1,0 | % {
$_.Matches.Value #this prints the exact match
$_.Context #output shows all context properties to be empty
$_.Context.PreContext[0] #this would ideally output first line before the match
$_.Context.PreContext[0] -eq $null #but instead is null
}
我是不是误会了什么?
匹配 "line3" 时 return "line2" 的最佳方法是什么?
谢谢!
编辑: 我忽略说明的其他要求: 需要为不确定长度的字符串提供所有匹配行上方的行。 EG 在下面搜索 "line3" 时,我需要 return "line2" 和 "line5".
line1
line2
line3
line4
line5
line3
line6
您可以使用多行正则表达式,使用 -match
运算符:
$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@
$pattern =
@'
(?m)
(.+?)
line3
'@
if ($teststring -match $pattern)
{ [Regex]::Matches($teststring,$pattern) |
foreach {$_.groups[1].value} }
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
$teststring -split '\r?\n'
将多行字符串拆分成一个行数组:- 注意:您的此处文档使用的换行符序列(LF-only 与 CRLF)取决于封闭的脚本文件;正则表达式
\r?\n
处理任一样式。
- 注意:您的此处文档使用的换行符序列(LF-only 与 CRLF)取决于封闭的脚本文件;正则表达式
请注意,使用管道提供
Select-String
的输入至关重要;如果您使用-InputObject
,数组将被强制转换回单个字符串。
Select-String
方便,但是慢.
特别是对于已经在内存中的单个字符串,使用 .NET Framework 的 [Regex]::Matches()
方法的解决方案将执行得更好,尽管它更复杂[=116] =].
请注意,PowerShell 自己的 -match
和 -replace
运算符构建在同一个 .NET class 上,但不会公开其所有功能; -match
- 它在自动 $Matches
变量中报告捕获组 - 在这里不是一个选项,因为它只 returns 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
}
正则表达式
(?:^|(.*)\n).*(line3)
使用 2 个捕获组 ((...)
) 捕获要匹配的行的(匹配部分)和之前的行 ((?:...)
是一个辅助非-捕获组,需要优先级):(?:^|(.*)\n)
匹配字符串的最开头 (^
) 或 (|
) 任何 - 可能为空 - 非换行符序列 (.*
) 后跟一个换行符 (\n
);这确保了当前面有 no 行时也能找到要匹配的行(即,要匹配的行是 first 行)。(line3)
是定义要匹配的行的组;它前面有.*
以匹配问题中的行为,其中找到模式line3
即使它只是一行的 部分 。- 如果您只想匹配 完整 行,请改用以下正则表达式:
(?:^|(.*)\n)(line3)(?:\n|$)
- 如果您只想匹配 完整 行,请改用以下正则表达式:
[Regex]::Matches()
找到 all 个匹配项,并且 returns 它们作为System.Text.RegularExpressions.Match
个对象的集合,ForEach-Object
cmdlet 调用然后可以操作以提取捕获组匹配 ($_.Groups[<n>].Value
).
[1] 撰写本文时:
- 不需要匹配 两次 - 封闭的 if ($teststring -match $pattern) { ... }
是不必要的。
- 不需要内联选项 (?m)
,因为 .
默认 不 匹配换行符 。
- (.+?)
仅捕获 非空行 (不需要非贪婪量词 ?
)。
- 如果感兴趣的行是 first 行 - 即,如果没有行 before,则不会匹配。