Programming, Technology, and Systems Administration

PowerShell ExpandProperty vs Property

I’m only an occasional PowerShell user, and therefore it’s taking some time to develop a deeper understanding of the language. I’m gonna share something that recently clicked for me that I think isn’t well explained elsewhere: using ExpandProperty, especially in combination with understand putting values vs Objects on the pipeline.

ExpandProperty is part of the Select-Object cmdlet, and it’s used to expand properties into the PowerShell pipeline. This is still a beginner-level guide, but if you find words like cmdlet and PipeLine confusing, you might want to go do a basic PowerShell tutorial first. Select-Object accepts a number or parameters, but I would argue that Property (which is the default if no parameter name is used) and ExpandProperty are the main two, and it’s not always easy for beginners to know when to use which, or to understand why you need to do this at all.

So let’s look at some examples. I have four different scenarios to cover, and in each I’ll show how these two Parameters work for that situation. For best results, you’ll want to follow along in a PowerShell console on your own machine.

We’ll use Get-Process throughout the examples as a data source, and for shorthand and to ensure consistent results if the system state changes, I’ll save it to a variable, like this:

$p = Get-Process

Collections

First up are collections. The Get-Process cmdlet returns objects with many properties. One of these is the “Modules” property, which happens to be a collection. Let’s look at that property:

$p | Select-Object -Property Modules

You’ll likely see a bunch of text that’s not very useful. You’ll also see a bunch of blanks, where processes that require administrator or System access were just skipped. Now run it like this:

$p | Select-Object -ExpandProperty Modules

This will likely produce some errors (again: for processes that require admin or System access), and that’s okay; we get a lot of results back, too. These results should be much more meaningful. In fact, let’s assign them to a variable:

$m = $p | Select-Object -ExpandProperty Modules

Now we can look at $m.Length. On my system, I see 2931 modules, more than you’re likely to get from any one process. So we see ExpandProcess rolled up the contents of the Modules collection from every object in the pipeline. We can also look at individual entries in $m, such $m[0], to get a feel for the kind of data we have now. And we can use $m | Get-Member to get the full list of properties and methods available on each object. Importantly, we see we’re working with real System.Diagnostics.ProcessModule objects, and not something more generic that happens to have similar properties, or a bad string representation.

Objects

Let’s try another example. In this case we’ll look at selecting a single item instead of a collection, but we’ll focus on the StartInfo property, which is itself an Object with several properties of it’s own.

$p | Select-Object -Property StartInfo

This produces a bunch of a lines on the console that all have the same “System.Diagnostics.ProcessStartinfo” text. That’s not very useful. Now let’s try ExpandProperty:

$p | Select-Object -ExpandProperty StartInfo

Wow, there’s a lot here. Let’s put this data into a variable so we can look more closely:

$info = $p | Select-Object -ExpandProperty StartInfo

Now I can check $info.Length and see a number that matches the number of processes. I can look at $info[0] and see a representation of that object. And I can look at $info | Get-Member to see the full list of items available for use this object. Most of all, Get-Member tells me I’m working with a real System.Diagnostics.StartInfo object. The entire object is there, with all of it’s fields, properties, and methods, and they are all in the pipeline.

Properties

But what if I wanted just the Id and Processname… say to eventually output to a CSV file, or to make the screen easier and quicker to read? In that case, I can do this:

$p | Select-Object -Property Id,ProcessName

This let me use just those two properties. There are some things to note here. First all, there is no ExpandProperty equivalent to show. The ExpandProperty parameter can only use one property at a time. Second, these are not System.Diagnostics.Process objects anymore, regardless of what Get-Member shows. They are a custom type with only the base methods (ToString(), etc) and the properties I asked for.

Values

Finally, let’s look at a simple property. Let’s say I have another cmdlet, and I need to give it the ProcessName as an argument. I could try this:

$p | Select-Object -Property ProcessName

but it wouldn’t work for most cmdlets. Let’s look closely at the output, especially near the top. You’ll see this:

ProcessName
-----------
[a bunch of process here]

That header means we’re still dealing with an object, just like in the previous example. This object has one property named “ProcessName”. Unfortunately, my cmdlet wanted a string value. So let’s do this instead:

$p | Select-Object -ExpandProperty ProcessName

It seems like the same data, but look again at the output at the top of the list. This time there’s no header. Now we’re dealing with simple string values, and that’s much more likely to match up with whatever cmdlet you’re using needs to see.

Conclusion

Hopefully this has been helpful. My intent was demonstrate not only the differences between Property and ExpandProperty, but also help you understand objects vs values, why that can matter, and to give you some tools to help you know which you’re dealing with.

Tags:

No Comments so far ↓

There are no comments yet...Kick things off by filling out the form below.