PowerShell output is crossing between functions
Solution 1:
tl; dr:
Force synchronous output to the console with Out-Host
:
getUsersAndGroups | Out-Host
getRunningProcesses | Out-Host
Note: You can alternatively use one of the Format-*
cmdlets, which also forces synchronous output; e.g., getUsersAndGroups | Format-Table
.
Inside a PowerShell session:
-
This is primarily a display problem, and you do not need this workaround for capturing output in a variable, redirecting it to a file, or passing it on through the pipeline.
-
Caveat: Sending to
Out-Host
means that the commands' output can no longer be captured or redirected; see this answer for a - suboptimal - workaround.
From the outside, when calling the PowerShell CLI (powershell -file ...
or powershell -command ...
):
-
Actual data loss may occur if
Out-Host
is not used, because pending asynchronous output may never get to print if the script / command ends withexit
; e.g.:# !! Prints only 'first' powershell.exe -command "'first'; [pscustomobject] @{ foo = 'bar' }; exit"
-
However, unlike in intra-PowerShell-session use,
Out-Host
fixes both the display and the data-capturing / redirection problem, becauseOut-Host
's output too is sent to stdout, as seen by an outside caller (but note that the for-display representations that PowerShell's output-formatting system produces aren't generally suitable for programmatic processing).
Note: All of the above also applies to PowerShell (Core) 7+ and its pwsh
CLI, up to at least v7.2.
The explanation of PowerShell's problematic behavior in this case:
It may helpful to demonstrate the problem with an MCVE (Minimal, Complete, and Verifiable Example):
Write-Host "-- before"
[pscustomobject] @{ one = 1; two = 2; three = 3 }
Write-Host "-- after"
In PSv5+, this yields:
-- before
-- after
one two three
--- --- -----
1 2 3
What happened?
-
The
Write-Host
calls produced output synchronously.-
It is worth noting that
Write-Host
bypasses the normal success output stream and (in effect) writes directly to the console - mostly, even though there are legitimate uses,Write-Host
should be avoided. -
However, note that even output objects sent to the success output stream can be displayed synchronously, and frequently are, notably objects that are instances of primitive .NET types, such as strings and numbers, as well as objects whose implicit output formatting results in non-tabular output as well as types that have explicit formatting data associated with them (see below).
-
-
The implicit output - from not capturing the output from statement
[pscustomobject] @{ one = 1; two = 2; three = 3 }
- was unexpectedly not synchronous:- A blank line was initially produced.
- All actual output followed the final
Write-Host
call.
This helpful answer explains why that happens; in short:
-
Implicit output is formatted based on the type of objects being output; in the case at hand,
Format-Table
is implicitly used. -
In Psv5+, implicitly applied
Format-Table
now waits up to 300 msecs. in order to determine suitable column widths.-
Note, however, that this only applies to output objects for whose type table-formatting instructions are not predefined; if they are, they determine the column widths ahead of time, and no waiting occurs.
-
To test whether a given type with full name
<FullTypeName>
has table-formatting data associated with it, you can use the following command:# Outputs $true, if <FullTypeName> has predefined table-formatting data. Get-FormatData <FullTypeName> -PowerShellVersion $PSVersionTable.PSVersion | Where-Object { $_.FormatViewDefinition.Control.ForEach('GetType') -contains [System.Management.Automation.TableControl] }
-
-
Unfortunately, that means that subsequent commands execute inside that time window and may produce unrelated output (via pipeline-bypassing output commands such as
Write-Host
) or prompt for user input beforeFormat-Table
output starts.- When the PowerShell CLI is called from the outside and
exit
is called inside the time window, all pending output - including subsequent synchronous output - is effectively discarded.
- When the PowerShell CLI is called from the outside and
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.
Note: This answer originally incorrectly "blamed" the PSv5+ 300 msec. delay for potentially surprising standard output formatting behavior (namely that the first object sent to a pipeline determines the display format for all objects in the pipeline, if table formatting is applied - see this answer).