1

I've been able to match multiple lines; but only if I know how many lines are coming, and what the content of those lines are...

Select-String -Pattern "^Timestamp: 3/27/2021.*`n`n|^Message:.*errorText:" -Context 2 -LiteralPath .\SomeLog.log

Is there a way to match multiple lines without knowing what is in between?

for instance to match

[START]
...
...
[END]

I read something about changing the settings to the regex with (?sme) but it doesn't seem to work.

I was trying something like the following:

Select-String -Pattern '(sme?)\[START\].*\n(.*\n)\+\[END\]'

leeand00
  • 25,510
  • 39
  • 140
  • 297
  • 3
    There is no `e` flag in .NET regex flavor, and `?` is placed right after `(`. Just use `Get-Content $fpath -Raw | Select-String -Pattern '(?s)\[START].*?\[END]' -AllMatches | % { $_.matches.value }` – Wiktor Stribiżew Nov 19 '21 at 22:44
  • 2
    See (prototype in) propose: [`#15136` Add `-From` and `-To` parameters to `Select-String`](https://github.com/PowerShell/PowerShell/issues/15136) – iRon Nov 20 '21 at 09:23
  • 1
    The file could have Windows format newlines (CRLF). You're now assuming it has *nix style (LF only) – Theo Nov 20 '21 at 15:13

1 Answers1

3

To make Select-String match multiline substrings:

  • You must provide the input as a single, multiline string, which is what Get-Content's -Raw switch provides.

  • As needed, in the regex passed to Select-String's -Pattern parameter, use inline regex option m (multi-line) to make ^ and $ match the beginning and end of each line ((?m)) and/or option s (single-line) to make . match newline characters ("`n") too ((?s)); you can activate both with (?sm).

Here's an example with a multiline here-string serving as input, instead of, say,
Get-Content -Raw file.txt:

(@'
before
[START]
...1
...2
[END]
after
[START]
...3
...4
[END]
done
'@ | 
  Select-String  -AllMatches -Pattern '(?sm)^\[START\]$.+?^\[END\]$'
).Matches.Value -join "`n------------------------`n"

Note: Strictly speaking, only [, not also ], requires escaping with \.

If you only want to find the first block of matching lines, omit -AllMatches.Thanks, Wiktor Stribiżew.
-AllMatches requests returning all matches per input string, and is normally - with line-by-line input - used to find multiple matches per line. Here, with a multiline input string, all (potentially multiline) matches inside it are returned.

Output:

[START]
...1
...2
[END]
------------------------
[START]
...3
...4
[END]

If you want to return only what is between the delimiter lines:

(@'
before
[START]
...1
...2
[END]
after
[START]
...3
...4
[END]
done
'@ | 
  Select-String  -AllMatches -Pattern '(?sm)^\[START\]\r?\n(.*?)\r?\n\[END\]$'
).Matches.ForEach({ $_.Groups[1].Value }) -join "`n------------------------`n"

Note: \r?\n matches both Windows-format CRLF and Unix-format LF-only newlines. Use \r\n / \n to match only the former / latter.

Output:

...1
...2
------------------------
...3
...4
mklement0
  • 382,024
  • 64
  • 607
  • 775