Even without a traditional user interface, it is important to separate the presentation layer from the data processing layer.
For all of its strengths, Windows PowerShell is not without its fair share of weaknesses. One of the weaknesses in PowerShell that has been with the scripting language since version 1.0 is how it handles formatting. When it comes to formatting data, you have two options: use one of the core Format-* cmdlets (Format-Table, Format-List, Format-Wide, or Format-Custom), or create a format ps1xml file that defines one or more formats for a specific type in an XML document. The former only works for the script consumer, because the core Format-* cmdlets actually convert rich object data into format data, not bearing any resemblance to the original objects with properties and methods that you started with. The latter works for the script author as well as the script consumer, however it is significantly more complicated to implement, and it has its share of limitations as well (for example, once a default format is defined for a type, a script author will not be able to have the results of their script rendered in another format by default without taking some extra steps that shouldn’t be necessary). The end result of these limitations and complications is a formatting system that is not fully leveraged in the majority of PowerShell scripts that are shared among the community.
I recently found myself looking at this problem and asking myself one more time if I could do better (it is a problem that I had looked at in the past). The variety of formats that are available is encouraging, and you can generate some pretty useful report-like output when you take the time to leverage the formatting system. But something would have to be done to fix the user experience. The end goal I was envisioning was quite clear: a distinct separation of the presentation layer from the data processing layer such that formatting information would never get in the way of data processing.
Fortunately, PowerShell is quite well set up for this type of change. Every command that you run has its results sent through the Out-Default cmdlet. Out-Default internally knows what to do with whatever you throw at it, whether that be object data or format data (which really is object data as well, but I’m making a distinction here). When you return an object from a command to the console, if that object is not format data, Out-Default internally will look at the object type, identify the default format for that type, convert the object into format data and then output that format data to the console. If Out-Default does receive format data instead because the object was already converted into format data, it simply renders that format data in the console. Given that is the case, it should be possible to modify the core Format-* cmdlets so that they attach format data to the objects that they format instead of converting object data into format data, and it should also be possible to modify the core Out-Default cmdlet so that it detects format data when it is attached to an object and renders that format data directly to the console instead of looking at the object type to decide what format to use. That, my friends, is exactly what I did.
FormatPx is a module made up of a nested binary module that defines five proxy cmdlets (Format-Table, Format-List, Format-Wide, Format-Custom, and Out-Default) plus one new cmdlet (Format-Default) and a script module that automatically applies the Force parameter whenever Format-Table, Format-List, or Format-Wide are used. It changes how the core Format-* cmdlets work as described above, and it also makes it easier to get format information from types that include a custom format as their default. Here’s a short screencast showing FormatPx in action:
(Note, do not adjust your computer, there is no audio in this screencast.)
That’s a decent overview of what you get with the FormatPx module – a separation of the formatting layer from the data processing layer, giving you much more control over the presentation of your script results without ps1xml file complexity while still allowing script consumers to view the results in whatever format they like. What do you think?