Search code examples
bashpowershellgrepselect-string

How to get actual separate lines in PowerShell's Write-Output using a newline character


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?


Solution

  • 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