I was working on a script trying to output two different custom objects; one after another and was having problems, so to simplify, I stripped all the code down to just the
When I do a
Get-Member
, it seems to just show one object.
Get-Member
shows information about the distinct types in the collection of inputs, which means that the first occurrence of each type is reported on, with subsequent occurrences skipped.
In your case, both input objects are of type [System.Management.Automation.PSCustomObject]
, so Get-Member
will report just that one shared type.
For instance, 1, 2 | Get-Member
reports information about System.Int32
once.
one object is a custom object like above, but the other it a selected.system.int32. When I try to output one after the other only the first one outputs.
PowerShell's default output formatting defaults to implicit use of Format-Table
for custom objects with up to 4 properties.
If you output multiple objects that have different types, and the first object defaults to implicit Format-Table
output, that first object's type alone determines what properties (columns) to show in the resulting table.[1]
If subsequent objects do not have any of the same properties that the first object has, they simply print a blank line; if they have some of the same properties, only those are printed; any additional properties are ignored.
It is important to note that this is just a display problem, however: all objects that were output are still present; if you send the output to another command for further processing instead of directly to the console, they'll all be there.
A simple example:
PS> [pscustomobject] @{ one = 1; two = 2 }, [pscustomobject] @{ three = 3 }
one two
--- ---
1 2
Note how the 2nd custom object resulted in just a blank line, because it has neither properties .one
nor .two
.
You can work around the problem by using explicit formatting commands applied to each object to output:
PS> [pscustomobject] @{ one = 1; two = 2 }, [pscustomobject] @{ three = 3 } |
ForEach-Object { Format-Table -InputObject $_ }
one two
--- ---
1 2
three
-----
3
The same approach as individual output commands:
[pscustomobject] @{ one = 1; two = 2 } | Format-Table
[pscustomobject] @{ three = 3 } | Format-Table
As Mark Wragg's blog post explains, all output produced by a given script - even across separate commands - is sent to the same pipeline.
(You can think of a command line submitted interactively as an implicit script.)
For a more detailed discussion of how a mix of types in a single pipeline is handled in terms of display formatting, see this answer.
Why using an explicit formatting command helps:
By explicitly piping to a Format-*
cmdlet (e.g, [pscustomobject] @{ one = 1; two = 2 } | Format-Table
), you're actually sending formatting objects (various [Microsoft.PowerShell.Commands.Internal.Format.*]
types) to the pipeline, and PowerShell then effectively passes them through for display.
An alternative is to use a generic workaround: if you pipe to Out-Host
instead (e.g., [pscustomobject] @{ one = 1; two = 2 } | Out-Host
), in which case:
It is important to note that these workarounds are suitable only for display purposes, because the original objects are lost in the process:
When you pipe to a Format-*
cmdlet explicitly, you replace the original object with objects containing formatting instructions, which are useless for further processing.
When you pipe to Out-Host
, you send nothing to the script's pipeline.
[1] If the first object's type happens to have formatting data associated with it (as reported by Get-FormatData), subsequent objects do print, albeit invariably via implicit Format-List
.
Try this:
$beep = new-object -TypeName PSObject
$beep | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "beep1"
$beep | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "beep1"
$beep | format-table
$boop = new-object -TypeName PSObject
$boop | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "boop1"
$boop | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "boop1"
$boop | format-table