Formatting issues / missing data when multiple com

2019-01-29 07:54发布

问题:

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?

回答1:

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


回答2:

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


回答3:

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 PSObjects 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 Strings and not PSObjects, this way when the script's output will be piped into Out-Default, it will pass strings through unprocessed.