我需要在多行字符串变量的匹配之前返回一行。
当使用字符串变量作为输入时,似乎Select-String认为整个字符串已匹配。因此,Context属性是" 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
}
我在这里误解了什么吗?
返回" line2"的最佳方式是什么?当匹配" line3"?
时谢谢!
编辑: 我忽略了附加要求: 需要在所有匹配的行上方提供一行不确定长度的行。 EG在下面搜索" line3"我需要返回" line2"和" line5"。
line1
line2
line3
line4
line5
line3
line6
答案 0 :(得分:5)
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'
将多行字符串拆分为一行数组:
\r?\n
处理任何一种风格。请注意,使用管道提供Select-String
的输入至关重要;如果您使用-InputObject
,则数组将被强制转换为单个字符串。
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
}
正则表达式(?:^|(.*)\n).*(line3)
使用2个捕获组((...)
)来捕获要匹配的行(匹配部分)和之前的行((?:...)
是辅助<优先级所需的em>非 - 捕获组:
(?:^|(.*)\n)
匹配字符串的最开头(^
)或(|
)任意 - 可能为空 - 非换行字符序列(.*
)跟随换行(\n
);这确保了当没有前一行时也找到要匹配的行(即,要匹配的行是第一个一行)。(line3)
是定义要匹配的行的组;它前面有.*
来匹配问题中的行为,其中找到了模式line3
,即使它只是行的部分。
(?:^|(.*)\n)(line3)(?:\n|$)
[Regex]::Matches()
找到所有匹配项,并将它们作为System.Text.RegularExpressions.Match
个对象的集合返回,ForEach-Object
cmdlet调用可以对其进行操作以提取捕获组匹配($_.Groups[<n>].Value
)。
[1]撰写本文时:
- 无需匹配两次 - 不需要附上if ($teststring -match $pattern) { ... }
- 不需要内嵌选项(?m)
,因为.
默认 。
- (.+?)
仅捕获非空行(并且?
,不需要非贪婪量词。)
- 如果感兴趣的行是第一行行 - 也就是说,如果之前没有行,则不会匹配。
答案 1 :(得分:1)
您可以使用多行正则表达式,-match
运算符:
$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@
$pattern =
@'
(?m)
(.+?)
line3
'@
if ($teststring -match $pattern)
{ [Regex]::Matches($teststring,$pattern) |
foreach {$_.groups[1].value} }