3

This is a spin-off from another question which is located here: Formatting an Object as a neatly looking list The basis of the argument, I believe, was wrong since we're not dealing with formatting of the object afterwards. That's only for looks as shown by the console but it can have bearing on the integrity of the object as you manipulate the variable containing the object.

What I need is to create an object that inherently outputs a list (not a table). I know it's possible because I've tested many functions that I've not written and the objects created are in fact lists. No need to use Format-List to distort or shape what's already there. I just have not being able to figure out why sometimes the output is a list or a table. I'm not sure where the magic is. However I do know that when I run $Host before I run the variable containing the created object I get the object that Host produces, which a list, and that shapes the object afterwards also as a list that normally would be shown as a table. Of course, that may give the result I want but I'm not looking to show the Host information. So what is the solution to this, I wish someone could explain this.

Mack
  • 149
  • 1
  • 8
  • Do you really expect to get a fast/precise answer to that wordy/noisy question which requires one to read also the other question? I did and and it's still unclear to me what you are after. Without any code and/or output, and what you expect I´m out. –  Oct 31 '16 at 00:52
  • Thanks for the comment. I'm just after what the title suggests. I just want to create an object that outputs a list, not a table. Forget about Format-List. So say you create an object and that object outputs a table or a list, what gives it that format in the first place? – Mack Oct 31 '16 at 01:03
  • 1
    PowerShell (the interpreter) does. By default objects with up to 4 properties are displayed in tabular form, objects with more than 4 properties are displayed in list form. For defining a custom format [start here](https://msdn.microsoft.com/en-us/library/gg580944.aspx). You'll probably need to define a [custom type name](https://learn-powershell.net/2012/03/02/working-with-custom-types-of-custom-objects-in-powershell/) too. Or, you could simply not bother and use `Format-List`. Take your pick. – Ansgar Wiechers Oct 31 '16 at 01:26
  • @AnsgarWiechers You are absolutely right. That seems to be the behavior. I will look at the material you have provided. Thank you for showing up in here too. – Mack Oct 31 '16 at 02:06
  • 2
    "*What I need is to create an object that inherently outputs a list (not a table). I know it's possible because I've tested many functions that I've not written and the objects created are in fact lists.*" - PowerShell is not bash, it has a separation of content and presentation, like HTML and CSS have. Functions output objects. Objects can be displayed as lists or tables or neither. Functions don't inherently output lists *or* tables. – TessellatingHeckler Oct 31 '16 at 03:26
  • Possible duplicate of [Powershell output formatting](http://stackoverflow.com/questions/9752531/powershell-output-formatting) – TessellatingHeckler Oct 31 '16 at 03:26
  • Can you turn the separation of content and presentation into an answer? – Walter Mitty Oct 31 '16 at 06:44

1 Answers1

12

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.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • 2
    I can't understand why this answer only had +1. Especially the part about keeping data "raw" is an important practice in Powershell. Data must be kept raw untill you output it (either to a file, the console, a gui form or anything else). It keeps your code clear and it prevents stupid beginner mistakes (like export-csv after a format-table). – bluuf Aug 11 '17 at 16:47
  • @Ansgar: A big thanks to you. I have gone through each and every line and all the articles shared by Jeffrey. Too many big things to grasp. I think this is by far the best explanation on formatting. The custom ps1xml part is just a cherry on top of the cream. Hats off. – Ranadip Dutta Feb 26 '19 at 11:05