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

1 Answer 1

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
}
Sign up to request clarification or add additional context in comments.

2 Comments

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...
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.