PSCustomObject: Save Puppies and Avoid Dead Ends

Welcome to Scripting Games 2013. Here’s my favorite hint for improving your functions and scripts. Avoid writing to the console or formatting your output. Instead use PSCustomObject in Windows PowerShell 3.0 and leave the formatting to the end user.

Windows PowerShell provides lots of great ways to return the output of a command or function. You can write to the host program (Write-Host), write to a file (Out-File), and format your output to look really pretty (Format-*). But all of these techniques kill puppies and bring the pipeline to an abrupt halt.

“Puppies?,” you ask. Yes! Windows PowerShell MVP and Scripting Games 2013 Viceroy Don Jones (@concentrateddon) famously says that every time you use Write-Host, a puppy dies. So sad!

The Format cmdlets are almost as bad, although no deaths have yet been attributed to them. Instead, when you use a Format cmdlet, a huge STOP sign should appear warning you that you’ve brought the pipeline to a halt. Not technically, of course, but for all practical purposes.

To see what I mean, take a peek at these two commands. The output of these commands looks very similar, but it’s really quite different.

These two commands return different objects and the difference really matters. To see the different output types, you can pipe them to Get-Member. I’ve used a slightly different approach that gets only the names of types in the output, but it’s the same idea.

Instead of a process object, the formatted command returns a bunch of format objects. You usually discover this when you try to use them in another command. For example, these format objects don’t have the properties of a process object, like PagedMemorySize or Handles.

So you’ve lost the opportunity to use these objects in subsequent commands. Unless you really want formatting object, the pipeline is effectively dead. Almost as sad as those puppies.

I realized this problem when some colleagues at Microsoft asked me to generate a report that listed the CDXML files in a CIM module and the CIM commands that were defined in each CDXML file. I wrote a tiny script that produced a nice report that looked like this:

But, instead of being delighted, they reported that they now had data that they couldn’t use. I had created a dead end. Pretty, but useless. They were happier with a command that produced useable results, even if they weren’t pretty.

To avoid this dead end in the silly Get-Process case, you just remove the Format-Table command. Or, you can use the Select-Object cmdlet to create an object that is a filtered subset of the current object, if that’s what you need.
But how do you manage when you’re returning values from different objects? It’s easy to put them in a table, but there’s a much better way that doesn’t stop the pipeline.

Windows PowerShell 3.0 introduces PSCustomObject. You can read all about it, and about Windows PowerShell 2.0 alternatives, in about_Object_Creation. PSCustomObject makes it easy for you to create objects.

As the name implies, PSCustomObject creates a custom object with the properties that you specify. The resulting custom object works just like any .NET class object, so you can pass it through the pipeline and use it in subsequent commands.

The value of PSCustomObject is a hash table (@{Key = Value; Key=Value…}) where the keys are property names and the values are property values. When you define a PSCustomObject hash table in a script or function, Windows PowerShell magically creates an object for every instance that you pass to it.

Here’s how I used it in a little script that tells you the versions of Updatable Help you have on your local machine.

In this case , I was processing a bunch of HelpInfo XML files. I want to return an object that contains the module name, the name of the UI culture, and the version number for that UI culture. The details don’t matter, except that the property values weren’t all in the same object, so I couldn’t just select from an object.

PSCustomObject to the rescue! See how easy this is!

In the ForEach loop, I get the values that I need. Then I just define a PSCustomObject and … voila! … I have my objects. The default formatting makes them look nice enough.

But more importantly, the pipeline continues. When I pipe to Get-Member, it shows that I have a usable custom object:

And, I can use the output in subsequent commands.

Now, go out and try it! Some of the Scripting Games challenges might require a table, list, or some other formatting, but if it doesn’t, be sure return a really useful object.

Good luck to everyone!

About June Blender

June Blender was a senior programming writer on the Windows PowerShell team at Microsoft from Windows PowerShell 1.0 – 3.0. You see her work every time you type Get-Help for the core modules. She's now working on the Windows Azure Active Directory SDK team, and she remains an avid Windows PowerShell user and a passionate user advocate. She's a guest blogger for the Scripting Guys and she tweets Windows PowerShell tips on Twitter at @juneb_get_help. A 16-year veteran of Microsoft, June lives in magnificent Escalante, Utah, where she works remotely when she's not out hiking, canyoneering, taking Coursera classes, or convincing lost tourists to try Windows PowerShell. She believes that outstanding documentation is a collaborative effort, and she welcomes your comments and contributions to Windows PowerShell and Windows Azure Help.

13 thoughts on “PSCustomObject: Save Puppies and Avoid Dead Ends

  1. John Hammer

    Great topic, I often see folks struggling with this because they do some formatting and then try to reuse that and are stuck with the dead end.

  2. matt

    “Here’s how I used it in a little script that tells you the versions of Updatable Help you have on your local machine.”

    And right there is where you lost me.

    I don’t understand this: “…the property values weren’t all in the same object, so I couldn’t just select from an object.”
    I don’t get it at all; I seem to be missing the core concept. You’re processing some objects that have properties, so you make a new object with those properties?

  3. June Blender Post author

    That’s it, exactly, Matt!

    The particular example isn’t important. The core idea is that you can use PSCustomObject to create objects even when the properties of the new object come from different objects and different sources. This characteristic distinguishes PSCustomObject from Select-Object and other easy techniques.

  4. Pingback: Delete all empty subfolders of Deleted Items | sidefumbling

  5. mc

    This is a nice post. Unless I’m missing something you didn’t show all the code for your example, i.e. I don’t know where $helpInfoFiles comes from but I understand the concept. Where I’m really lost is where $u comes from, because I don’t understand where you specify the object name.

    So here’s how I found this article. I am trying to create a PSObject or PSCustomObject where the name of the new object is the value of a parameter or even the property value of another object. I want to create a new object using the name of each value in an existing object that was created by importing a CSV file (although I guess the source is hardly relevant)
    To try and explain:

    $props = @{    p1="one";    p2="two"}$newobjectname = "testobjectname"$newobjectvariablename = "$" + $newobjectname#so that newobjectvariablename should be $testobjectnameforeach ($newobj in $newobjectvariablename) {     $newobj = New-Object -TypeName PSObject -Property $props}# Now I should have an object of type PSCustomObject with properties p1 and p2 with values of "one" and "two" respectively. This doesn't work.# Instead I have an object called $newobj with the properties and values I needed in $newtestobjectname

    I’ve also tried things like:
    ($newobjectvariablename | Out-String).ToString() = New-Object …
    $newobjectvariablename.ToString() = New-Object…

    Is this even possible or did I just completely miss something obvious? Thanks.

    1. Mike Hanson

      $sVarName = “Things”
      $sVarValue = “Lots of Stuff”
      New-Variable $sVarName -Value $sVarValue
      $Things | GM

      1. Mike Hanson

        More correctly, try this:
        $Props = @{
        $NewObjectName = “TestObjectName”
        New-Variable -Name $NewObjectName -Value $Props

  6. Pingback: InvokeRestMethod for the Rest of Us - Hey, Scripting Guy! Blog - Site Home - TechNet Blogs

  7. Pingback: SAPIEN Technologies » Blog Archive » Get-LanguageKeywords: Using ConvertFrom-String on About Topics

  8. Pingback: SAPIEN Technologies » Blog Archive » Enumerators in Windows PowerShell 5.0

Comments are closed.