Wednesday, November 27, 2013

PowerShell’s Single Formatting Pipeline

In some of my recent training, I’ve been encouraging delegates to create scripts, and to try to crate them to be as reusable as possible. In class, I’ve seen some very confused students – confused by the output.

Here’s a simple example of the confusion I sometimes see:

PSH [C:\foo]: ls wf1.*

    Directory: C:\foo

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         10/8/2012   4:48 PM       1614 wf1.ps1

PSH [C:\foo]: get-service WAS

Status   Name               DisplayName
------   ----               -----------
Running  WAS                Windows Process Activation Service

PSH [C:\foo]: ls wf1.*;get-service WAS

    Directory: C:\foo

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         10/8/2012   4:48 PM       1614 wf1.ps1

Status      : Running
Name        : WAS
DisplayName : Windows Process Activation Service

I’ve also seen this behavior in functions, for example:

Function Get-Stuff {
  Get-Service WAS
  Get-Process PowerShell
  ls cert:\

I’ll leave the details of the output as an exercise for the reader – but calling Get-Stuff suddenly switches from a table view to a list view.  Here’s another thing I’ve recently seen:

PSH [C:\foo]: gwmi win32_share ; gcim win32_bios

Name                              Path         Description
----                              ----         -----------
ADMIN$                            C:\Windows   Remote Admin
C$                                C:\          Default share
N$                                N:\          Default share
Phoenix ROM BIOS PLUS Version ...              Phoenix ROM BIOS PLUS Version ...

So what’s gong on here? We see what would normally be a table being displayed as a list and vice versa.

Really, it’s very simple. At the end of any command sequence, the last command in the pipeline (or for that matter the first command in a single command pipeline!), can emit objects. When the pipeline is ‘finished’ – PowerShell uses the first object type emitted to determine how to format everything else in the pipeline. Or that’s the plan. Unfortunately, when putting out totally different object types, this formatting probably  can’t work since the later object(s) lack the properties that the display XML used for the first object would have wanted to display. So PowerShell just produces a more basic list view. IN the last example, the formatting subsystem DID have enough properties to crate a (rather odd) table view from what is normally a list view.

For script or function developers, the clear answer is to always create a single object type and return that and only that object type. The examples above are returning different object types and PowerShell's formatting subsystem is perhaps less intelligent than we might have hoped for. I’m not knocking it, just pointing out one of the minor downsides.

One way to get around this is to end each command sequence that explicitly leaves objects in the pipeline with a call to Format-Table/Format-List like this:

PSH [C:\foo]: gwmi win32_share |ft ; gcim win32_bios | fl

Name                         Path                              Description
----                         ----                              -----------
ADMIN$                       C:\Windows                        Remote Admin
C$                           C:\                               Default share
N$                           N:\                               Default

Manufacturer      : Dell Inc.
Name              : Phoenix ROM BIOS PLUS Version 1.10 A00
SerialNumber      : 6Y84C3J
Version           : DELL   - 15

So remember that when any script or function returns objects, PowerShell does a ‘best efforts” attempt to display the returned objects. For a while lot of reasons, such scripts or functions are better off returning just one type of object – a custom object if nothing else will do.


Technorati Tags: ,

No comments: