How to create an Object that outputs a List (Not A Table)

PowerShell does some default formatting when it presents data/objects to the user. Usually objects are displayed in tabular form when they have up to 4 properties, and in list form when they have more than 4 properties.

If you output several things in a row, PowerShell applies the format (list/table) from the first object to all subsequent objects. I don’t know the exact reasoning behind this behavior, but presumably it’s to make the output more consistent.

Demonstration:

PS C:\> $o1 = New-Object -Type PSObject -Property @{a=1;b=2;c=3;d=4;e=5}
PS C:\> $o2 = New-Object -Type PSObject -Property @{x='foo';y='bar'}
PS C:\> $o1

c : 3
e : 5
d : 4
b : 2
a : 1

PS C:\> $o2

y                                    x
-                                    -
bar                                  foo

PS C:\> $o1; $o2

c : 3
e : 5
d : 4
b : 2
a : 1

y : bar
x : foo

Beware, however, that relying on this behavior might lead to undesired results if you output objects in the wrong order:

PS C:\> $o2; $o1

y                                    x
-                                    -
bar                                  foo         # ← properties of $o2
                                                 # ← empty line for $o1!

$o1 appears as a blank line in the above output, because outputting $o2 first establishes tabular output format with the columns y and x, but $o1 doesn’t have these properties. Missing properties are displayed as blank values in tabular output, whereas additional properties are omitted from the output. There are also cases where you might get the output from the second object/list in list form (run for instance Get-Process; Get-ChildItem in a PowerShell console).

You can force subsequent objects or object arrays to be displayed as separate tables (or lists) by piping them through the Format-Table (or Format-List) cmdlet:

PS C:\> $o2; $o1 | Format-Table

y                                    x
-                                    -
bar                                  foo


              c            e            d            b            a
              -            -            -            -            -
              3            5            4            2            1

You can also force PowerShell to display each variable individually by piping them through (for instance) Out-Default:

PS C:\> $o2 | Out-Default; $o1 | Out-Default

y   x
-   -
bar foo

c : 3
e : 5
d : 4
b : 2
a : 1

Note, however, that this writes to the console, so the resulting output can’t be captured, redirected, or pipelined anymore. Use this only if you want to display something to a user.

For additional information about PowerShell output formatting see here.


There are ways to change the default behavior of how an object is displayed, but unfortunately they’re not exactly straightforward. For one thing you can define a default display property set to have PowerShell display not all properties, but just a particular subset.

PS C:\> $props="c", 'd'
PS C:\> $default = New-Object Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$props)
PS C:\> $members = [Management.Automation.PSMemberInfo[]]@($default)
PS C:\> $o1 | Add-Member MemberSet PSStandardMembers $members
PS C:\> $o1

                c                    d
                -                    -
                3                    4

You can still get all properties displayed by using Format-List *:

PS C:\> $o1 | Format-List *

c : 3
e : 5
d : 4
b : 2
a : 1

Defining the default display property set doesn’t allow you to define the output format though. To do that you probably need to write a custom formatting file. For that to work you probably also need to define a custom type for your objects.

$formatFile = "$HOME\Documents\WindowsPowerShell\Your.Format.ps1xml"
$typeFile   = "$HOME\Documents\WindowsPowerShell\Your.Type.ps1xml"

@'
<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
  <ViewDefinitions>
    <View>
      <Name>Default</Name>
      <ViewSelectedBy>
        <TypeName>Foo.Bar</TypeName>
      </ViewSelectedBy>
      <ListControl>
        ...
      </ListControl>
    </View>
  </ViewDefinitions>
</Configuration>
'@ | Set-Content $formatFile
Update-FormatData -AppendPath $formatFile

@'
<?xml version="1.0" encoding="utf-8" ?>
<Types>
  <Type>
    <Name>Foo.Bar</Name>
    <Members>
      ...
    </Members>
  </Type>
</Types>
'@ | Set-Content $typeFile
Update-TypeData -AppendPath $typeFile

$o2.PSTypeNames.Insert(0, 'Foo.Bar')

Jeffrey Hicks wrote an article series on the subject that you may want to read.


With all of that said, I would not recommend going this route unless you have very compelling reasons to do so. I tried to explain it before, but @TesselatingHeckler put it much more concisely than me, so I’m going to quote him on this:

PowerShell is not bash, it has a separation of content and presentation, like HTML and CSS have.

What you normally want to do in PowerShell is keep your data in objects, and have the properties of these objects contain the “raw” (i.e. unformatted) data. That gives you the most flexibility for processing your data. Formatted data usually only gets in the way, because it forces you to parse/convert the data again. Format your data only when you need to display it to a user, and use the Format-* cmdlets to do so. And if your output is intended for further processing: don’t bother formatting it in the first place. Leave it to the user how he wants to display the data.

Leave a Comment