String PowerShell:选择匹配前的行--选择字符串-使用输入字符串变量时的上下文问题

String PowerShell:选择匹配前的行--选择字符串-使用输入字符串变量时的上下文问题,string,powershell,match,select-string,String,Powershell,Match,Select String,我需要在多行字符串变量的匹配之前返回一行 似乎在为输入使用字符串变量时,Select字符串会将整个字符串视为已匹配。因此,上下文属性位于字符串任意一端的“外部”,并且为null 考虑以下示例: $teststring = @" line1 line2 line3 line4 line5 "@ Write-Host "Line Count:" ($teststring | Measure-Object -Line).Lines #verify PowerShell does regard inp

我需要在多行字符串变量的匹配之前返回一行

似乎在为输入使用字符串变量时,Select字符串会将整个字符串视为已匹配。因此,上下文属性位于字符串任意一端的“外部”,并且为null

考虑以下示例:

$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”时,返回“line2”的最佳方式是什么

谢谢

编辑: 我忽略了说明的附加要求: 对于长度不确定的字符串,需要在所有匹配行上方提供行。例如,在下面搜索“line3”时,我需要返回“line2”和“line5”


您可以使用带有
-match
运算符的多行正则表达式:

$teststring = @"
line1
line2
line3
line4
line5
line3
line6
"@

$pattern = 
@'
(?m)
(.+?)
line3
'@


if ($teststring -match $pattern)
  { [Regex]::Matches($teststring,$pattern) |
    foreach {$_.groups[1].value} }

选择字符串
对输入数组进行操作,因此必须为
-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与CRLF)取决于随附的脚本文件;regex
      \r?\n
      处理这两种样式
  • 请注意,使用管道提供
    选择字符串的输入是至关重要的;如果使用了
    -InputObject
    ,数组将强制返回到单个字符串



选择字符串
很方便,但速度很慢。

特别是对于已经在内存中的单个字符串,使用.NET Framework方法的解决方案将执行得更好,尽管它更复杂

请注意,PowerShell自己的
-match
-replace
操作符构建在同一个.NET类上,但并不公开其所有功能
-match
-在自动
$Matches
变量中报告捕获组-此处不是选项,因为它只返回1个匹配

下面的方法与答案中的方法基本相同,但纠正了几个问题[1]

  • Regex
    (?:^ |(.*))。*(第3行)
    使用两个捕获组(
    (…)
    )捕获要匹配的行的(匹配部分)和之前的行(
    (?:…)
    是一个辅助的非捕获组,需要优先):

    • (?:^ |(.*)\n)
      匹配字符串的最开始(
      ^
      )或(
      |
      )任何可能为空的非换行字符序列(
      *
      ),后跟换行符(
      \n
      );这确保了在没有前一行时也能找到要匹配的行(即,要匹配的行的前一行是第一行)
    • (line3)
      是定义要匹配的行的组;它前面有
      *
      ,以匹配问题中的行为,其中模式
      line3
      ,即使它只是一行的一部分。
      • 如果只希望匹配整行,请使用以下正则表达式:
        (?:^ |(.*)\n)(第3行)(?:\n |$)
  • [Regex]::Matches()
    查找所有匹配项并将其作为对象集合返回,然后
    ForEach-Object
    cmdlet调用可以对其进行操作,以提取捕获组匹配项(
    $\uu0.Groups[].Value


[1] 截至撰写本文时:
-不需要两次匹配-如果($teststring-match$pattern){…}
不需要封闭的
if($teststring-match$pattern){…}

-不需要内联选项
(?m)
,因为默认情况下
与换行符不匹配。
-
(.+?)
只捕获非空行(不需要非贪婪量词)。
-如果感兴趣的行是第一行-即,如果之前没有行,则不会匹配

line before:  line2
matched part: line3
line before:  line5
matched part: line3
# 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
}