Prof. Powershell

Practical PowerShell Part 4: Output Formatting

The journey from a single-line command to a useable tool continues.

More on this topic:


If you've been following along the last few weeks, you've seen how I started with a simple one-line command and have created a (hopefully) useful tool. Although honestly, I hope the journey is more valuable than the destination. When we left last time, I had a script that wrote a custom object to the pipeline. This is the relevant section of code.

New-Object -TypeName psobject -Property @{
RegisteredApp = $entry
RegisteredPath = $regentry.$entry
AppDescription = $app.ApplicationDescription
AppName = $app.ApplicationName
}

In PowerShell 3 and later one option many of you might consider, and one that frankly I've used often, is to use an ordered hashtable. Here's the same section of code revised:

$hash= [ordered]@{
RegisteredApp = $entry
RegisteredPath = $regentry.$entry
AppDescription = $app.ApplicationDescription
AppName = $app.ApplicationName
}

  New-Object -TypeName psobject -Property $hash

You have to define the ordered hashtable first. This works and now when I run the script the properties will be displayed in the order listed. Another option is to use the [pscustomobject] type accelerator, also available in v3 and later.

[pscustomobject]@{
RegisteredApp = $entry
RegisteredPath = $regentry.$entry
AppDescription = $app.ApplicationDescription
AppName = $app.ApplicationName
}

However, using an ordered hashtable locks you in to that particular order and set of properties. In addition, ordered hashtables can be a bit more resource intensive. In this case, the performance hit is unnoticeable, but it could be significant when it comes to processing a larger number of objects.

Instead, the better approach is to separate output formatting from your tool. In PowerShell v3 and later this is actually quite easy. You can modify type and format extensions on the fly. With my current project since I only have a small number of properties, I can get by with defining a custom type. Of course, I first need to add the type to my object.

Let me return to an earlier version of my script.

#requires -version 3.0
#define registry property values to ignore
$exclude = "PSParentPath","PSChildName","PSDrive","PSProvider","PSPath"

$regentry = Get-ItemProperty -Path HKLM:\SOFTWARE\RegisteredApplications | Select -Property * -ExcludeProperty $exclude

#get property names
$props = $regentry.psobject.properties.name
foreach ($entry in $props) {
#create the registry path to check
$valuepath = Join-Path -path "HKLM:" -child $regentry.$entry

Try {
#follow the value to the corresponding registry key
$app = Get-ItemProperty -path $valuepath -ErrorAction Stop |
Select ApplicationDescription,ApplicationName
}
Catch {
$app=$Null
}

#write an object to the pipeline
$obj = New-Object -TypeName psobject -Property @{
Computername = $env:computername
RegisteredApp = $entry
RegisteredPath = $regentry.$entry
AppDescription = $app.ApplicationDescription
AppName = $app.ApplicationName
}

  #add the custom type name
  $obj.psobject.TypeNames.Insert(0,"My.RegisteredApp")

#write result to the pipeline
$obj
}

 

As each object is created, I assign it a new type name, My.RegisteredApp. This is inserted at position 0 in the array of Typenames so PowerShell will look for a corresponding type and/or format extension. Here's how easy it is to add that piece:

Update-TypeData -TypeName My.RegisteredApp -DefaultDisplayPropertySet Computername,RegisteredApp,RegisteredPath,AppDescription,AppName -Force

Now when I run my script, PowerShell will automatically use this list of default properties which because there are five or more results in a list.

[Click on image for larger view.]  Figure 1.

If I decide I want a different order, all I need to do is update the type data. I never have to go back and modify the script, which is very important. Everytime I go back and modify a script that already works, I run the risk of breaking something and spending time fixing it.

If I wanted, I could even add code to the beginning of my script to add the type extension if it was missing.

$myType = "My.RegisteredApp"
$ddps = "Computername","RegisteredApp","RegisteredPath","AppDescription","AppName"

if (-Not (Get-TypeData -TypeName $myType)) {
Update-TypeData -TypeName $myType -DefaultDisplayPropertySet $ddps -Force

}

Although then you are back to having to modify the script file if you need to make change. However you want to handle it, we've taken a single PowerShell command and turned it into something practical. Although I can think of one more step which you can probably guess based on the change I made to the object output. But that is for next time.

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.

comments powered by Disqus
Most   Popular