Write-Host
is the right tool for producing (possibly colored) for-display output - as opposed to outputting data via PowerShell's success output stream, via cmdlet calls and expressions, (optionally via explicit Write-Output
calls, but that's rarely needed).
This answer explains that if you mix Write-Host
and success-stream output, in PowerShell v5+ what prints to the console can appear out of order.
This is a side effect of implicitly applied tabular formatting situationally being asynchronous, in an effort to collect some data before printing output so as to determine suitable column width. It happens only for output types that (a) don't have predefined format data, and (b) have 4 or fewer properties (because types with more properties default to list formatting).
The problematic behavior is discussed in GitHub issue #4594; while there's still hope for a solution, there has been no activity in a long time.
There is no good solution to this problem as of PowerShell 7.0:
There are two - suboptimal - workarounds:
(a) Pipe individual commands that trigger the asynchronous behavior to ... | Out-Host
.
E.g., in the following command the command with the Select-Object
call must be sent to Out-Host
so as to appear correctly between the two Write-Host
calls on the screen:
Write-Host '------- 1'
Get-Item . | Select-Object FullName | Out-Host
Write-Host '------- 2'
Downside: Using Out-Host
means you lose the ability to capture or redirect the command's output, because it is sent directly to the host (display). Additionally, it is cumbersome to (a) know what commands trigger the problem and (b) to remember to apply the workaround to each.
(b) Replace Write-Host
calls with sending strings with embedded VT (Virtual Terminal) escape sequences (for coloring) to the success output stream.
Note: Requires Windows PowerShell v5.1 on Windows 10 or PowerShell [Core] v6+
Downside: The (colored) strings become part of the code's data output and are therefore included when you capture / redirect output.
# Windows PowerShell 5.1: [char] 0x1b produces an ESC char.
$green = [char] 0x1b + '[32m'; $reset = [char] 0x1b + '[m'
# Print "green" in green.
"It ain't easy being ${green}green${reset}."
# PowerShell 6+: `e can be used inside "..." for ESC.
$yellow = "`e[33m"; $reset = "`e[m"
# Print "yellow" in yellow.
"They call me mellow ${yellow}yellow${reset}."
- The fact that these strings contain ESC chars. could actually be used to filter out for-display strings from the data stream (assuming your actual data doesn't contain ESC chars.), along the lines of
... | Where-Object { -not ($_ -is [string] -and $_ -match '\e') }
Embedding VT escape sequences allows you to selectively color parts of your strings.
Achieving the same effect with Write-Host
would require multiple calls with -NoNewline
.
Third-party cmdlet (module) Write-ColoredOutput
emulates Write-Host
's syntax and uses the [console]
type's attributes to turn coloring on and off, while sending the string to the success output stream.
This works well for writing an entire string in a given color, but you cannot piece together differently colored parts on a single line, because each string individually written to the success output stream invariably prints on its own line.
If you wanted a convenience wrapper around embedding VT sequences directly in strings, you could adapt the Write-HostColored
function from this answer, by replacing the Write-Host
calls that happen behind the scenes with VT sequences.