More – How does PowerShell formatting really work?

<Edited 7/2/2006 with tags and categories>
PSMDTAG:FAQ: Why doesn’t output expand when I expand the width of the console?
PSMDTAG:FAQ: Why doesn’t output expand when I use -noElements on GROUP?

Consider the example:

PS> gps |group company

Count Name                      Group
—– —-                      —–
   34 Microsoft Corporation     {alg, CcmExec, ctfmon, dsamain…}
    1 Adobe Systems Incorpor… {apdproxy}
    2 Alps Electric Co., Ltd.   {ApntEx, Apoint}
    4                           {csrss, Idle, System, winlogon}
    1 Alcor Micro, Corp.        {DrvMon}
    2 Microsoft (R) Corporation {FwcAgent, FwcMgmt}
    3 Computer Associates In… {InoRpc, InoRT, InoTask}
    1 Intel Corporation         {ZCfgSvc}

Notice that the Company Name is truncated in the output.  This blog will address 2 questions:

  1. Why doesn’t this get expanded when I expand the width of the console?
  2. Why doesn’t this get expanded when I use -noElements on GROUP?

Let’s review the basic model.  In the Console host (PowerShell.exe), cmdlets emit objects which go to the out-default.  Out-Default looks at the type of the object and looks for registered views for this type and calls the appropriate formatter.  If you ever want to find the view defintion for the object you find the object’s PSTypeNames then look for those entries in the *FORMAT.PS1XML files in $PSHOME.  Note that the out-default will walk down the array of PSTypeNames until it finds a match so if you don’t find a view for the first value in this array, search for the next.

PS> (gps |group company)[0].pstypenames

PS> cd $pshome

PS> Select-String Microsoft.PowerShell.Commands.GroupInfo *format.ps1xml

PowerShellCore.format.ps1xml:93:            <Name>Microsoft.PowerShell.Comm
PowerShellCore.format.ps1xml:95:                <TypeName>Microsoft.PowerSh
PowerShellCore.format.ps1xml:128:            <Name>Microsoft.PowerShell.Com
PowerShellCore.format.ps1xml:130:                <TypeName>Microsoft.PowerS
PowerShellCore.format.ps1xml:917:            <Name>Microsoft.PowerShell.Com
PowerShellCore.format.ps1xml:919:                <TypeName>Microsoft.PowerS
PowerShellCore.format.ps1xml:946:            <Name>Microsoft.PowerShell.Com
PowerShellCore.format.ps1xml:948:                <TypeName>Microsoft.PowerS

PS> Notepad PowerShellCore.Format.ps1xml

Here is that you’ll find in PowerShellCore.Format.ps1xml:



What this is showing you is that you have a view which is selected by the groupinfo type, that this object should be rendered as a table, it defines the headers and the data for that table.  Examine the TableHeaders section and you’ll see that the first 2 TableColumnHeaders provide a LABEL and a WIDTH but the last one does not.  When no LABEL is specified, the name of the property is used

One of the great things about Windows PowerShell is its transparency and discoverability The views are critical to that.  The output is what tells the user what properties they can expect to get from an object.  If your label is different than the object, then you misset their expectations.  We realized this after the fact otherwise there is a strong chance we would not have allowed you to use a LABEL.  Now one of the reasons why you might be tempted to use a label is because in the data definitions, you might want to use a SCRIPT to calculate a value – THIS IS A BAD IDEA TOO [I know because I’ve stepped on that rake].  If you ever find yourself wanting to do this, the best thing to do is to extend the type itself with a scriptproperty and then specify that script property in formating.

The way formatting works is that it calculates the widths of the fields based upon the data in this view and the width of the console host.  You can see this by doing a dir of some long names and then change your CONSOLE properties to vary the WIDTH of the console (this is under the LAYOUT TAB).  Make it large and small and do the same diff every time and you’ll see what I mean. 

The formatter looks at the console width and then allocates the space explicitly reserved by the view (in this case Count reserves 5 spaces and Name reserves 25) and then allocates the remaining space evenly between all the other fields. This explains a couple of things:

  1. If you make your console very wide, the GROUP column will show more data but the NAME column won’t (because it asked for fixed width of 25)
  2. PowerShell tries to put those fields whose width will vary the most in the rightmost columns. This minimizes the visual variance of the display as you change the width of your console (We thought all this stuff through).

You might think that specifying -NoElement on the group command will fix this.  The GROUP command creates GroupInfo objects which give you a NAME (which is the VALUE of the property you are groupping by), the COUNT, and then the GROUP.  The GROUP is an array of all the original elements (objects) that where grouped into this GroupInfo.  There are lots of cases where that is really useful but it can be expensive so we provided the -NOELEMENT Switch which does not create this array.  That said, we still emit a GROUPINFO object and thus it still binds the the same view and thus it still allocates 25 characters for the Name.

There are a couple of ways around this:

  1. You can pipe things into format-table and specify count,name -auto
    PS> gps |group company |ft count,name -auto
  2. You can define your own view in your own FORMAT.PS1XML file and then ask for that view.  Make sure you give it a different name (e.g. MyView) and then ask for that view
    PS> gps |group company |ft -View MyView

Jeffrey Snover
Windows PowerShell Architect

PSMDTAG:CMDLET:FORMAT: truncating data, use of formatting metadata
PSMDTAG:INTERNAL: How formating uses metadata
PSMDTAG:CMDLET:FORMAT: How formating uses metadata