Using Formatting Files with PowerShell 7

Any terminal output in PowerShell is controlled by formatting files. Oftentimes, it is very useful to display more information about a given object than the default views offer. Since the early days of PowerShell, there has been an option to modify this display by using Format.ps1xml files.

The biggest change to formatting, in recent years, has been that PowerShell 6 defined all the default views for objects within the actual PowerShell source code. Prior to this, the views were defined in formatting files as well.

Formatting File Structure

The formatting file uses XML to define the views. Within the file, four different views can be defined.

  • Table, which corresponds to the cmdlet Format-Table
  • List, which corresponds to the cmdlet Format-List
  • Wide, which corresponds to the cmdlet Format-Wide
  • Custom, which corresponds to the cmdlet Format-Custom

A typical format file uses the following XML tags to define a view.

  • <Name> is a unique identifier given to the view
  • <ViewSelectedBy> defines what object type or types that the view applies to
  • <GroupBy> will define how objects are grouped within a specified view
  • <TableControl><ListControl><WideControl>, and <CustomControl> are tag containers for defining how each property of an object is displayed in the terminal

Getting Started

The easiest way to get started is to use a default view and then modify that view as necessary. Thankfully PowerShell offers us a few functions that make this process much easier.

  • Get-FormatData
  • Export-FormatData
  • Update-FormatData

Export Existing Format File

In this article, we are going to update the display of the System.IO.FileInfo and store the resulting change in a file named FileInfo.Format.ps1xml. The naming format of Type.Format.ps1xml is generally used, but technically the file can be named anything.

Get-FormatData -TypeName 'System.IO.FileInfo' | Export-FormatData -Path "$HOME\\Format\\FileInfo.Format.ps1xml"

It is only required that PowerShell have access to the directory where the formatting files are stored. The default $HOME location is a commonplace to store the format files. In this case, a Format folder would need to be created.

Updating the View

Unfortunately, for this type, no default properties are exported. We have a blank slate and can build a view however we like.

Image # Expand
Untitled 2020 03 29T113543.488

First, we need to define a name for our view, which in this case we will call Custom.System.IO.FileInfo. This name could be anything, but prepending the type name with Custom helps to differentiate the view when using the view from the command line.

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <ViewDefinitions>
		<View>
      <Name>Custom.System.IO.FileInfo</Name>
      <ViewSelectedBy>
				<TypeName>System.IO.DirectoryInfo</TypeName>
        <TypeName>System.IO.FileInfo</TypeName>
      </ViewSelectedBy>
		</View>
	</ViewDefinitions>
</Configuration>

At this point, we have only defined a name and what object types are affected by this format file. Next we need to select a basic set of properties to show. For the below example, only two properties are defined to test the output, that of FullName and Extension.

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <ViewDefinitions>
		<View>
      <Name>Custom.System.IO.FileInfo</Name>
      <ViewSelectedBy>
				<TypeName>System.IO.DirectoryInfo</TypeName>
        <TypeName>System.IO.FileInfo</TypeName>
      </ViewSelectedBy>
      <ListControl>
        <ListEntries>
          <ListEntry>
            <ListItems>
              <ListItem>
                <PropertyName>FullName</PropertyName>
              </ListItem>
              <ListItem>
                <PropertyName>Extension</PropertyName>
              </ListItem>
            </ListItems>
          </ListEntry>
        </ListEntries>
      </ListControl>
		</View>
	</ViewDefinitions>
</Configuration>

At this point, you might wonder why we are using two different TypeName values. This is due to the fact that when running Get-ChildItem, where this view is most likely to be used, the cmdlet itself outputs a header using the System.IO.DirectoryInfo type and then all subsequent objects as System.IO.FileInfo. To make this work without error, both types need to be specified in this case.

Load Updated Type Data

Since we have defined an updated view for the List view of the type System.IO.FileInfo, we can go ahead and load this updated view into PowerShell.

Update-FormatData -AppendPath "$HOME\\Format\\FileInfo.Format.ps1xml"

When loading formatting data, there are two parameters to be aware of. There is PrependPath and AppendPath. These two parameters control the order of loading when updating views. When using PrependPath, the views loaded will take precedence over the default views. When using AppendPath, the views are loaded after the default views.

Viewing the Formatting Changes

To verify that our custom view is loaded, run Get-FormatData and select that of System.IO. Without filtering the results, all formatting data will be output, which is a lot.

Image # Expand
Untitled 2020 03 29T113605.357

Simply running the function Get-ChildItem on the directory will not show the changes to the formatting data. To do this, we can send the output to the Format-List cmdlet and specify our custom view.

Image # Expand
Untitled 2020 03 29T113618.652

The view that we have created up to this point is not terribly useful, so let’s update to make it a bit more useful. One very specific set of functionality that should be called out is the ability to add a ScriptBlock tag into your views. By doing so, you can add a lot of advanced functionality. In the following example, we are merely showing a reformatted LastWriteTime.

<?xml version="1.0" encoding="utf-8"?>
<Configuration>
  <ViewDefinitions>
		<View>
      <Name>Custom.System.IO.FileInfo</Name>
      <ViewSelectedBy>
        <TypeName>System.IO.DirectoryInfo</TypeName>
        <TypeName>System.IO.FileInfo</TypeName>
      </ViewSelectedBy>
      <ListControl>
        <ListEntries>
          <ListEntry>
            <ListItems>
              <ListItem>
                <PropertyName>FullName</PropertyName>
              </ListItem>
              <ListItem>
                <PropertyName>Extension</PropertyName>
              </ListItem>
              <ListItem>
                <Label>LastWriteTime</Label>
                <ScriptBlock>'{0:d} {0:HH}:{0:mm}' -f $_.LastWriteTime</ScriptBlock>
              </ListItem>
            </ListItems>
          </ListEntry>
        </ListEntries>
      </ListControl>
    </View>
	</ViewDefinitions>
</Configuration>

The ScriptBlock tag will operate on each item, hence using the $_ current item identifier. Additionally, you do not need to wrap the contents of the ScriptBlock within braces as this is implied.

Image # Expand
Untitled 2020 03 29T113635.797

Conclusion

As you can see, it’s easy to update formatting data within PowerShell to create unique and useful custom views. There is a lot more functionality that one could give to a specified view. In addition, modules can include type format data for any custom objects that are created within that view. This allows a more user-friendly experience, “out-of-the-box” that enhances a module’s usefulness.