How to Select-String multiline?

11,262

Solution 1

Select-String operates on its input objects individually, and if you either pass a file path directly to it (via -Path or -LiteralPath) or you pipe output from Get-Content, matching is performed on each line.

Therefore, pass your input as a single, multiline string, which, if it comes from a file, is most easily achieved with Get-Content -Raw (PSv3+):

Get-Content -Raw file.txt | Select-String -Pattern Line1

Note that this means that if the pattern matches, the file's entire content is output, accessible via the output object's .Line property.


By contrast, if you want to retain per-line matching but also capture a fixed number of surrounding lines, use the -Context parameter.

Get-Content file.txt | Select-String -Pattern Line1 -Context 0, 2

Ansgar Wiechers' helpful answer shows how to extract all the lines from the result.

Solution 2

Select-String allows to select a given number of lines before or after the matching line via the parameter -Context. -Context 2,0 selects the preceding 2 lines, -Context 0,2 selects the subsequent 2 lines, -Context 2,2 selects the 2 lines before as well as the 2 lines after the match.

You won't get match and context lines in one big lump, though, so you need to combine matched line and context if you want them as a single string:

Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
    $($_.Line; $_.Context.PostContext) | Out-String
}

As @mklement0 correctly pointed out in the comments, the above is comparatively slow, which isn't a problem if you're only processing a few matches, but becomes an issue if you need to process hundreds or thousands of matches. To improve performance you can merge the values into a single array and use the -join operator:

Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
    (,$_.Line + $_.Context.PostContext) -join [Environment]::NewLine
}

Note that the two code snippets don't produce the exact same result, because Out-String appends a newline to each line including the last one, whereas -join only puts newlines between lines (not at the end of the last one). Each snippet can be modified to produce the same result as the other, though. Trim the strings from the first example to remove trailing newlines, or append another newline to the strings from the second one.

If you want the output as individual lines just output the Line and PostContext properties without merging them into one string:

Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
    $_.Line
    $_.Context.PostContext
}
Share:
11,262
vanillacoke9191
Author by

vanillacoke9191

Updated on July 18, 2022

Comments

  • vanillacoke9191
    vanillacoke9191 almost 2 years

    I am trying to Select-String on a text that is on multiple lines.

    Example:

     "This is line1
     <Test>Testing Line 2</Test>
     <Test>Testing Line 3</Test>"
    

    I want to be able to select all 3 lines in Select-String. However, it is only selecting the first line when I do Select-String -Pattern "Line1". How can I extract all 3 lines together?

    Select-String -Pattern "Line1"
    
  • metablaster
    metablaster about 3 years
    Select-String operates on its input objects individually this is critical point!
  • js2010
    js2010 over 2 years
    Also weird things happen at the prompt vs a script. At the prompt, select-string looks for \r at the ends of lines of raw strings in windows, and there's the problem of pasting tabs or inserting literal tabs at the prompt.