I've run into a problem with my PowerShell scripts where I am "losing" output that was going to the console, or being redirected. After much legwork, I simplified it down to an easily documented example of the problem.
Place the following single line in a script (test1.ps1), and invoke it from a basic PowerShell command window:
Get-WmiObject win32_share | select name, path
and you will likely get two columns of output similar to the following:
PS C:\Users\me\temp> ./test1.ps1
name path
---- ----
ADMIN$ C:\Windows
C$ C:\
IPC$
Users C:\Users
but now, create a two line script (test2.ps1) like this, and run it in the same manner:
Get-WmiObject win32_share | select name
Get-WmiObject win32_share | select name, path
and observe the output gets mangled:
PS C:\Users\me\temp> ./test2.ps1
name
----
ADMIN$
C$
IPC$
Users
ADMIN$
C$
IPC$
Users
The two commands are actually run , but the second call gets "squeezed" into the output of the first. It lost its second column, and its header and spacing.
This really seems to be a problem of the "first" output not properly "closing out", because it really doesn't seem to matter what the second line is if it pipes into another select statement.
As an aside, my original lines looked a bit more like this:
Get-WmiObject win32_share | select name, path | Write-Output
Get-WmiObject win32_share | select name | Write-Output
So that from a powershell prompt, I could direct the output wherever I desired using redirection
PS C:\Users\me\temp\> ./test2.ps1 > ./outfile.txt
As stated, actual case is more complez than this, but at this point, I just want to get a understanding of what is happening.
Bug? Or failure to comprehend on my part?
The link from mjolinor explains it in more detail but PowerShell is trying to group your outputs together here. An important line from the post states:
If the first Object has 2 properties, you'll get a 2 column table even if all the other objects in the stream have 10 properties.
In the following example
Get-WmiObject win32_share | select name
Get-WmiObject win32_share | select name, path
When the seconds command has run the output from the previous did not have that property so PowerShell, more specifically Out-Default
drops it in output in order to make all the output synchronous.
I would be more curious what your real code was that was creating this issue. Like expirat001 shows you can convert the output to string and this won't happen since they are no longer objects. Be careful doing so as you can lose the original objects this way. Either way the data is not lost. Just not being displayed the way you expect.
Referring back to the example from before we can pretend the first line didn't have a path property. Calling it from select
anyway would just create the null column but would allow the other output to be displayed.
# In this next command "path" is not a valid property but PowerShell will create the column anyway
Get-WmiObject win32_share | select name, path
Get-WmiObject win32_share | select name, path
This is cheating but you could sent one of the commands to a different output stream.
Get-WmiObject win32_share | select name
Get-WmiObject win32_share | select name, path | Out-Host
Get-WmiObject win32_share | select name | ft
Get-WmiObject win32_share | select name, path | ft
Get-WmiObject win32_share | ft name
Get-WmiObject win32_share | ft name, path
The link from mjolinor indeed explains a lot, the main issue here is that you are launching a script via & ./test2.ps1
and the output of this command is piped into Out-Default
. In turn, Out-Default
sees a set of PSObject
s generated by Select-Object
cmdlets, and examining the first object (this is the first object returned by Get-WmiObject win32_share | select name
) it sees that it has only one property "name" and subsequently formats a table out of resultant objects gathering only name
property out of all of them.
To circumvent, use ft
or Format-Table
cmdlet to turn the output of each line into a sequence of String
s and not PSObject
s, this way when the script's output will be piped into Out-Default
, it will pass strings through unprocessed.