1

I just came across a funny behavior when using Write-Host on powershell.

What I wanted to do is get the colored output from Write-Host and concurrently save the result in a variable. Checking other SO questions, led me to try the following:

$zz = &{
   Write-Warning "hello"
   Write-Error "hello"
   Write-Output "hi"
} 3>&1 2>&1

$zz 
#WARNING: hello
#Write-Error: hello
#hi

$zz = &{ Write-Host -ForegroundColor Red "[ERROR]: Package id: ""jaja""  not found!"; } 3>&1 2>&1
# [ERROR]: Package id: "jaja"  not found!

$zz
# [nothing]

enter image description here

The output was surprising, and I could not find a way to have the output saved into a variable while also see it displayed, unlike when using the other Write-xxx commandlets.

Q: What's gong on and how can I get both the output shown and save the results into a variable?


REFERENCES:


UPDATE-1

Thanks to mklement0's updated answer, I also tried the following, which almost does what I want, but does not produce the color.

Write-Host -ForegroundColor Red "[ERROR]: Package id: ""jaja""  not found!" 6>&1 | Tee-Object -Variable zz

($zz = Write-Host -ForegroundColor Red "[ERROR]: Package id: ""$package""  not found!" 6>&1)

enter image description here

The conclusion seem to be that any coloring information is lost when using anything that has to do with redirecting output from Write-Host.


UPDATE-2

Interestingly, the color information are still "there" somewhere. Following mklement0's suggestion, I tried to save the color info for 2 different lines. But then the parsing is not correct, as shown below.

So with:

$captured = &{ Write-Host -ForegroundColor Red -NoNewline "[ERROR]: some error! " 6>&1; Write-Host -ForegroundColor Green "OKAY"  6>&1 }

We get:

enter image description here

not2qubit
  • 14,531
  • 8
  • 95
  • 135

1 Answers1

1

As explained in the answer you link to, you need redirection 6>&1 in order to capture Write-Host output (only works in PowerShell v5 and above):

  • Write-Host output captured via 6>&1 consists of one or more System.Management.Automation.InformationRecord instances, which print as if they were strings, namely by their .MessageData.Message property value, which is the string content of the argument(s) passed to Write-Host.

  • Therefore, any coloring that stems from the use of the -ForegroundColor and -BackgroundColor parameters is not (directly) passed through:

    • However, the information is preserved, namely in the .MessageData.ForegroundColor and .MessageData.BackgroundColor properties, along with the information about whether -NoNewLine was passed to Write-Host, in Boolean property .MessageData.NoNewLine
  • By contrast, coloring via ANSI / VT escape sequences embedded in the original string argument(s) is preserved.

Thus, you can recreate the original coloring as follows - note that Write-Host is again used:

$captured = Write-Host -ForegroundColor Red "[ERROR]: some error" 6>&1

$captured | ForEach-Object {
  $messageData = $_.MessageData
  $colorArgs = @{}
  if (-1 -ne $messageData.ForegroundColor) { $colorArgs.ForegroundColor = $messageData.ForegroundColor }
  if (-1 -ne $messageData.BackgroundColor) { $colorArgs.BackgroundColor = $messageData.BackgroundColor }
  Write-Host -Object $captured @colorArgs -NoNewline:$messageData.NoNewLine
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Wow, cool. (I seriously would never have tried to learn powershell if it was not for all your awesome SO contributions. Thank you! ✨) I tried the above with 2 different `Write-Host` objects, but then the parsing is not quite right, as I showed in OP update. It would have been cool to be able to have a *function* or *filter* which could spit out the original objects, regardless how many. OTH, I am now thinking that perhaps it's just easier to write your own ANSI coloring functions and pray they don't generate garbage on the users terminal... – not2qubit Dec 10 '22 at 03:35
  • I'm glad to hear it, and I appreciate the nice feedback, @not2qubit. As for your update: the output _is_ correct, as it also tries to recreate the effects of the `-NoNewLine` switch. Remove `-NoNewline:$messageData.NoNewLine` if you don't want that. Defining a `function` or `filter` based on the above is trivial, but for such a function / filter to handle _other_ types meaningfully too, more work would be needed. – mklement0 Dec 10 '22 at 03:51