To get the column names - which may or may not be property names - of the table view that is presented for a given .NET type if it has predefined formatting data (that includes a table view) associated with it:
- Note:
- The following is a proper, but nontrivial and limited solution that derives the column names from the formatting data, using the first table-view definition found. It also has conceptual background information.
- See the bottom section for a quick-and-dirty solution for getting the column names only, which uses text parsing to extract the column names directly from a given command's formatted output.
- The middle section builds on this first section and extracts a list of property names and calculated properties mirroring the column definitions, which can be used with
Select-Object
, in order to create custom objects that have properties with the same values that the formatting data produces.
# Determine the .NET type of interest.
$type = (Get-Process)[0].GetType()
# Extract the names of the column headers from the *first* table-view definition.
Get-FormatData $type -PowerShellVersion $PSVersionTable.PSVersion |
ForEach-Object FormatViewDefinition |
Where-Object Control -is [System.Management.Automation.TableControl] |
Select-Object -First 1 |
ForEach-Object {
$i = 0
$rows = $_.Control.Rows
foreach ($colLabel in $_.Control.Headers.Label) {
if ($colLabel) { $colLabel } # Explicit label, with a calculated column value or renamed property
else { $rows.Columns[$i].DisplayEntry.Value } # Property name, with its value as the column value.
++$i
}
}
Caveat: The above limits output to the first table-view definition found - which may or may not apply to a given command. Which definition is chosen by default is potentially governed by criteria associated with the definitions that select based on runtime conditions, including selecting by specific input type, given that a single instance of formatting data can cover multiple types.
Also note that views may involve grouping (as you see in Get-ChildItem
's formatted output, for instance), and the grouping criterion isn't covered by the command above.
Note that even for a single type multiple views may be defined, and in order to use a non-default one you must request it explicitly, via Format-Table
's -View
parameter, assuming you know the name,[1] e.g. Get-Process | Format-Table -View StartTime)
.
- See also:
- This answer for how to inspect formatting data in full.
- You can alternatively pipe
Get-FormatData
output to Export-FormatData
in order to export formatting data to an XML file, which has the disadvantage of being hard to read, but has the advantage of matching the XML schema used for authoring formatting data - see next point - whereas the in-memory types used to represent formatting data partially use property names that don't match the underlying XML elements.
- As for authoring formatting data, which as of PowerShell 7.2.2 requires XML files (
*.Format.ps1xml
):
Note:
Using -PowerShellVersion $PSVersionTable.PSVersion
with Get-FormatData
is only needed in Windows PowerShell, for certain types, to work around a bug that is no longer present in PowerShell (Core) 7.1+
While column names typically correspond to the property names of the type instances being formatted, that isn't always the case, such as with the [System.Diagnostics.Process]
instances output by Get-Process
.
- A general caveat, as zett42 notes, is that display formatting of types isn't part of the public contract regarding breaking changes, so formatting definitions are allowed to change over time.
If a given type has no predefined formatting data associated with it (in which case Get-FormatData
is a quiet no-op):
The names of its (public) instance properties are used as column
names.
You only get a table view by default if there are 4 or fewer properties but you can request it explicitly with Format-Table
(With 5 or more properties, Format-List
is applied by default).
To get the names of all (public) instance properties of a given object, use the intrinsic .psobject
property, which is a rich source of reflection; e.g.:
(Get-Process | Select-Object -First 1).psobject.Properties.Name
To create a list of property names and calculated properties usable with Select-Object
that mirror the formatting-data's column definition:
# Determine the .NET type of interest.
$type = (Get-Process)[0].GetType()
# Get an array of property names / calculated properties from the
# formatting data, for later use with Select-Object
$props =
Get-FormatData $type -PowerShellVersion $PSVersionTable.PSVersion |
ForEach-Object FormatViewDefinition |
Where-Object Control -Is [System.Management.Automation.TableControl] |
Select-Object -First 1 |
ForEach-Object {
$i = 0
$rows = $_.Control.Rows
foreach ($colLabel in $_.Control.Headers.Label) {
if ($colLabel) { # Explicit label, with a calculated column value or renamed property
@{
Name = $colLabel
Expression = if ('ScriptBlock' -eq $rows.Columns[$i].DisplayEntry.ValueType) {
[scriptblock]::Create($rows.Columns[$i].DisplayEntry.Value)
} else {
$rows.Columns[$i].DisplayEntry.Value
}
}
}
else { # Property name, with its value as the column value.
$rows.Columns[$i].DisplayEntry.Value
}
++$i
}
}
# Sample call
Get-Process | Select-Object -Property $props | Format-Table | more
The sample call produces similar output to just Get-Process
alone, as it uses the column definitions as (calculated) properties - albeit with default values for formatting attributes such as column width and alignment.
Note the explicit use of Format-Table
to ensure tabular output; without it - given that the [pscustomobject]
instances created by Select-Object
have no formatting data associated with them - list formatting (implied Format-List
) would result.
As Mathias points out, the calculated properties will be string-typed even for columns based on numeric properties, because their purpose in the formatting data is to created formatted string representations.
Quick-and-dirty solution for getting the column names only:
The following uses Out-String
-Stream
in conjunction with Select-String
to extract the column names from a given command's formatted output, which relies on two assumptions:
- The column names have no embedded spaces
- The command actually produces table-formatted output by default; however, you can insert a
Format-Table
call before Out-String
, if desired.
Get-Process | Out-String -Stream | Select-String -List '^\s*--+' -Context 1, 0 |
ForEach-Object { -split $_.Context.PreContext[0] }
Output:
NPM(K)
PM(M)
WS(M)
CPU(s)
Id
SI
ProcessName
Note: In Windows PowerShell an additional property shows, as the first one: Handles
.
[1] While tab-completion does offer view names, they appear to be out of sync with the actually available ones, as of PowerShell 7.2.2. To see the latter, provoke an error with a dummy name, and the error message will list the available ones; e.g. Get-Process | Format-Table -View NoSuch
lists the following available views in the resulting error message: process, Priority, StartTime