'PowerShell output is crossing between functions
I am writing a PowerShell script in version 5.1 on Windows 10 that gets certain pieces of information about a local system ( and eventually its subnets ) and outputs them into a text file. At first, I had all of the aspects in a single function. I ran into output issues when outputting getUsersAndGroups and getRunningProcesses functions, where output from getUsersAndGroups would be injected into the output of getRunningProcesses.
The two functions are:
# Powershell script to get various properties and output to a text file
Function getRunningProcesses()
{
# Running processes
Write-Host "Running Processes:
------------ START PROCESS LIST ------------
"
Get-Process | Select-Object name,fileversion,productversion,company
Write-Host "
------------- END PROCESS LIST -------------
"
}
Function getUsersAndGroups()
{
# Get Users and Groups
Write-Host "Users and Groups:"
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | Foreach-Object {
$groups = $_.Groups() | Foreach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
$_ | Select-Object @{n='Username';e={$_.Name}},@{n='Group';e={$groups -join ';'}}
}
}
getRunningProcesses
getUsersAndGroups
When I call getUsersAndGroups after getRunningProcesses, the output looks like this ( does not output getUsersAndGroups at all ):
Running Processes:
------------ START PROCESS LIST ------------
Name FileVersion ProductVersion Company
---- ----------- -------------- -------
armsvc
aswidsagenta
audiodg
AVGSvc
avgsvca
avguix 1.182.2.64574 1.182.2.64574 AVG Technologies CZ, s.r.o.
conhost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
csrss
csrss
dasHost
dwm
explorer 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
hkcmd 8.15.10.2900 8.15.10.2900 Intel Corporation
Idle
igfxpers 8.15.10.2900 8.15.10.2900 Intel Corporation
lsass
MBAMService
mDNSResponder
Memory Compression
powershell_ise 10.0.14393.103 (rs1_release_inmarket.160819-1924) 10.0.14393.103 Microsoft Corporation
RuntimeBroker 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
SearchFilterHost
SearchIndexer
SearchProtocolHost
SearchUI 10.0.14393.953 (rs1_release_inmarket.170303-1614) 10.0.14393.953 Microsoft Corporation
services
ShellExperienceHost 10.0.14393.447 (rs1_release_inmarket.161102-0100) 10.0.14393.447 Microsoft Corporation
sihost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
smss
spoolsv
sqlwriter
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
System
taskhostw 10.0.14393.0 (rs1_release.160715-1616) 10.0.14393.0 Microsoft Corporation
ToolbarUpdater
wininit
winlogon
WtuSystemSupport
WUDFHost
------------ END PROCESS LIST ------------
Users and Groups:
When I call getUsersAndGroups before getRunningProcesses the output of getUsersAndGroups is injected in getRunningProcesses and worse, no running processes are listed at all, but rather a lot of blank lines.
How can I separate or control the output of getUsersAndGroups so that it outputs before the output of getRunningProcesses?
The output of the injected output looks like this:
Running Processes:
------------ START PROCESS LIST ------------
Username Group
-------- -----
Administrator Administrators
debug255 Administrators;Hyper-V Administrators;Performance Log Users
DefaultAccount System Managed Accounts Group
Guest Guests
------------ END PROCESS LIST ------------
Thank you so much for your help!
Solution 1:[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-Hostmeans 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-Hostis not used, because pending asynchronous output may never get to print if the script / command ends withexit- see GitHub issue #13985; e.g.:# !! Prints only 'first' powershell.exe -command "'first'; [pscustomobject] @{ foo = 'bar' }; exit"However, unlike in intra-PowerShell-session use,
Out-Hostfixes 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-Hostcalls produced output synchronously.It is worth noting that
Write-Hostbypasses the normal success output stream and (in effect) writes directly to the console - mostly, even though there are legitimate uses,Write-Hostshould 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-Hostcall.
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-Tableis implicitly used.In Psv5+, implicitly applied
Format-Tablenow 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-Tableoutput starts.- When the PowerShell CLI is called from the outside and
exitis 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).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
