Prof. Powershell
Perfect PowerShell Properties Part 3: Custom Formatting
Here's how to jump through the complicated hoops to update format data in PowerShell.
- By Jeffery Hicks
- 05/13/2014
I hope you've been following along the last few weeks as we've been looking at ways to extend PowerShell's type system. If not, take a few minutes to get caught up (part 1 and part 2). This week we'll look at adding a custom formatting option.
Although PowerShell makes it easy to update type data, updating format data is not as easy. First, let's look at the existing format definitions for file objects. We can see existing format data with the Get-FormatData cmdlet.
PS C:\> get-formatdata -TypeName system.io.fileinfo | select -ExpandProperty FormatViewDefinition
Name Control
---- -------
children TableControl
children ListControl
children WideControl
You can interpret this to mean that there are existing definitions whether you use Format-Table, Format-List or Format-Wide. What I want to do is add a custom view. Given the number of properties I want to display and for the sake of simplicity I'm going to create a List view. This is going to require me creating a ps1xml file. You could copy this XML to a file.
$fmtxml = @'
<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
<ViewDefinitions>
<View>
<Name>MyFileData</Name>
<ViewSelectedBy>
<TypeName>System.IO.FileInfo</TypeName>
</ViewSelectedBy>
<ListControl>
<ListEntries>
<ListEntry>
<ListItems>
<ListItem>
<PropertyName>FullName</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Directory</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Mode</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Name</PropertyName>
</ListItem>
<ListItem>
<PropertyName>Extension</PropertyName>
</ListItem>
<ListItem>
<Label>Size</Label>
<PropertyName>Length</PropertyName>
</ListItem>
<ListItem>
<Label>Modified</Label>
<PropertyName>LastWriteTime</PropertyName>
</ListItem>
<ListItem>
<Label>ModifiedAge</Label>
<ScriptBlock>(Get-Date) - $_.lastWriteTime</ScriptBlock>
</ListItem>
<ListItem>
<Label>Created</Label>
<PropertyName>CreationTime</PropertyName>
</ListItem>
<ListItem>
<Label>CreationAge</Label>
<ScriptBlock>(Get-Date) - $_.CreationTime</ScriptBlock>
</ListItem>
</ListItems>
</ListEntry>
</ListEntries>
</ListControl>
</View>
</ViewDefinitions>
</Configuration>
'@
Or do as I did last time and create the XML file using PowerShell. You only have to create the file once, unless you decide to make changes.
$file = Join-Path -path $home\documents\windowspowershell -child MyFileFormat.ps1xml
$fmtxml | Out-File -FilePath $file
One major item I want to point out in the XML is that because I am defining some properties using script blocks, I need to reference $_. In order for PowerShell to not think I'm trying to use a variable in the here string, I am using single quotes, not double. The XML will use some existing properties, like Name. A few properties I am simply renaming.
<ListItem>
<Label>Size</Label>
<PropertyName>Length</PropertyName>
</ListItem>
Although, if I have my custom type definition that I showed you in an earlier lesson, I could have reference that. I also am creating some calculated properties.
<Label>CreationAge</Label>
<ScriptBlock>(Get-Date) - $_.CreationTime</ScriptBlock>
</ListItem>
All that remains is to tell PowerShell to use these new formatting definitions.
Update-FormatData -AppendPath $file
Re-checking format definitions shows my new view.
PS C:\> Get-FormatData -TypeName system.io.fileinfo | Select -ExpandProperty FormatViewDefinition
Name Control
---- -------
children TableControl
children ListControl
children WideControl
MyFileData ListControl
The view won't be used until I specify it along with the appropriate format cmdlet.
PS C:\> dir c:\work -file | format-list -View MyFileData
Here is a fragment from the result:
Figure 1
The downside to only having this as a formatting option, is that I can't do anything else with this other than send it to a text file or printer. So while I wanted you to know how this works, personally I would prefer to do everything as a type extension. Then I can format as I want, export, convert or whatever else needs to be done.
So to complete the story, these are the commands I would put into my PowerShell profile.
Update-TypeData -MemberType ScriptProperty -MemberName SizeKB -Value {$this.Length/1KB} -TypeName system.io.fileinfo
Update-TypeData -MemberType ScriptProperty -MemberName SizeMB -Value {$this.Length/1MB} -TypeName system.io.fileinfo
Update-TypeData -MemberType AliasProperty -MemberName Size -Value Length -TypeName system.io.fileinfo
Update-TypeData -MemberType AliasProperty -MemberName Created -Value CreationTime -TypeName system.io.fileinfo
Update-TypeData -MemberType AliasProperty -MemberName Modified -Value LastWriteTime -TypeName system.io.fileinfo
Update-TypeData -MemberType ScriptProperty -MemberName ModifiedAge -Value {(Get-Date) - $this.LastWriteTime} -TypeName system.io.fileinfo
Update-TypeData -MemberType ScriptProperty -MemberName CreationAge -Value {(Get-Date) - $this.CreationTime} -TypeName system.io.fileinfo
And I would create property sets.
$xml=@"
<?xml version="1.0" encoding="utf-8" ?>
<Types>
<Type>
<Name>System.IO.FileInfo</Name>
<Members>
<PropertySet>
<Name>MyFileData</Name>
<ReferencedProperties>
<Name>Name</Name>
<Name>Size</Name>
<Name>ModifiedAge</Name>
<Name>CreationAge</Name>
</ReferencedProperties>
</PropertySet>
<PropertySet>
<Name>MyFileDataKB</Name>
<ReferencedProperties>
<Name>Name</Name>
<Name>SizeKB</Name>
<Name>ModifiedAge</Name>
<Name>CreationAge</Name>
</ReferencedProperties>
</PropertySet>
<PropertySet>
<Name>MyFileDataMB</Name>
<ReferencedProperties>
<Name>Name</Name>
<Name>SizeMB</Name>
<Name>ModifiedAge</Name>
<Name>CreationAge</Name>
</ReferencedProperties>
</PropertySet>
</Members>
</Type>
</Types>
"@
$file = Join-Path -path $home\documents\windowspowershell -child MyFileType.ps1xml
$xml | Out-File -FilePath $file
Update-TypeData -AppendPath $file
Now I can use these properties any way I like.
You can download a text file of my extensions here.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.