0

I tried to create a multiline Input to practice Select-String, expecting only a single matching line to be output, like I would normaly see it in an echo -e ... | grep combination. But the following command still gives me both lines. It seems to be the newline is only interpreted on final ouptut and Select-String still gets a single line of input

Write-Output "Hi`nthere" | Select-String -Pattern "i"
#
# Hi
# there
#
#

while I would expect it to return just

Hi

I used this version of PowerShell:

Get-Host | Select-Object Version
# 5.1.19041.906

Comparing with bash I would do the following for testing commands on multiline input in bash. I usually generate multiple lines with echo -e and then grep processes the individual lines.

echo -e "Hi\nthere" | grep "i"
# Hi

I hope someone can explain what I miss here in PowerShell? This problem seems like a basic misconception to me, where I also was not sure what to Google for.

Edits

[edit 1]: problem also for line ending with carriage return

Write-Output "Hi`r`nthere" | Select-String -Pattern "i"

I saw that separating with commas works as valid multiline input. So maybe the question is how to convert from newline to actual input line separation.

Write-Output "Hi","there" | Select-String -Pattern "i"
# Hi

[edit 2]: from edit 1 I found this stackoverflow-answer, where for me it now works with

Write-Output "Hi`nthere".Split([Environment]::NewLine) | Select-String -Pattern "i"
# or
Write-Output "Hi`nthere".Split("`n") | Select-String -Pattern "i"

Still may someone please explain why this is relevant here, but not in bash?

Martin
  • 493
  • 6
  • 16
  • 1
    `echo -e …` in bash: `-e` means _Enable interpretation of [some backslash-escaped characters](https://ss64.com/bash/echo.html)_. PowerShell equivalent (roughly, incomplete): ```Write-Output ("Hi`nthere" -split "`r|`n") | Select-String -Pattern "i"``` – JosefZ Dec 27 '21 at 12:07
  • 2
    `Select-String operates on its input objects individually`, which means that for your purposes you need to split the multiline string into individual lines first using ``(("Hi`nthere" -split '\r?\n') | Select-String -Pattern "i").Line``. For an explanation, see [this answer](https://stackoverflow.com/a/55374303/9898643) – Theo Dec 27 '21 at 12:16
  • Thanks @JosefZ and @Theo and especially @JosefZ for pointing me towards the detailed explanation of "-e" which seems to be the actual explanation I was looking for. So `-e` in bash is equivalent to `-split "\`r|\`\n")` – Martin Dec 27 '21 at 12:20
  • Rethinking this... I actually favor `-split "\`r?\`n" more – Martin Dec 27 '21 at 12:28

1 Answers1

0

All the information is in the comments, but let me summarize and complement it:

PowerShell's pipeline is object-based, and Select-String operates on each input object - even if that happens to be a single multi-line string object, such as output by Write-Output "Hi`nthere"

  • It is only the output from external programs that is streamed line by line.

Therefore, you must split your multi-line string into individual lines in order to match them as such.

The best idiom for that is -split '\r?\n', because it recognizes both Windows-format CRLF and Unix-format LF-only newlines :

"Hi`nthere" -split '\r?\n' | Select-String -Pattern "i"

Note:

  • I've omitted Write-Output in favor of PowerShell's implicit output behavior (see the bottom section of this answer for more information).

  • For more information on how -split '\r?\n' works, see this answer.

  • Select-String doesn't directly output the matching lines (strings); instead it wraps them in match-information objects that provide metadata about each match. To get just the matching line (string):

    • In PowerShell (Core) 7+, add the -Raw switch.
    • In Windows PowerShell, pipe to ForEach-Object Line or wrap the entire call in (...).Line
mklement0
  • 382,024
  • 64
  • 607
  • 775