PSPropertyExpression type

Microsoft.PowerShell.Commands.PSPropertyExpression type (it’s type accelerator – [pspropertyexpression]) exists in PowerShell from the beginning, but in version 6.1 it was made public, what makes it accessible to us, as a script and module authors.

This type is used, for example, by the Property parameter of the Measure-Object cmdlet.

Get-Process pwsh | Measure-Object -Property CPU -Average
Count             : 3
Average           : 39.8645833333333
Sum               :
Maximum           :
Minimum           :
StandardDeviation :
Property          : CPU

In this case, as a -Property parameters value, we specified a string, but, the specificity of this type is that besides the strings, it can accept scriptblocks, and this allows us to pick for measurements not only already existing properties of object, but also the results of their transformations.

Get-Process pwsh | Measure-Object -Property {$_.Threads.Count} -Sum
Count             : 3
Average           :
Sum               : 71
Maximum           :
Minimum           :
StandardDeviation :
Property          : $_.Threads.Count

PowerShell 6.2 have brought us the Join-String cmdlet, that also uses PSPropertyExpression as a value type of the -Property parameter.

Get-Service R* | Join-String -Property { $_.DisplayName + "`n" + $_.Description } -Separator "`n`n"
Remote Access Auto Connection Manager
Creates a connection to a remote network whenever a program references a remote DNS or NetBIOS name or address.

Remote Access Connection Manager
Manages dial-up and virtual private network (VPN) connections from this computer to the Internet or other remote networks. If this service is disabled, any services that explicitly depend on it will fail to start.

...

Also, this type is implicitly used by several cmdlets, and one of which is Select-Object.

Using PSPropertyExpression

Now let’s take a look at how we can use the Microsoft.PowerShell.Commands.PSPropertyExpression type.

But firstly, we get the pwsh process object, which we will use in the following examples.

$ps = Get-Process pwsh | Select-Object -First 1

Property

We can create PSPropertyExpression object as follows.

$PathProperty = [pspropertyexpression]::new("Path")

To get the value of the specified property (in this case it is – Path) we use the GetValues method.

$PathProperty.GetValues($ps)
Result                                 ResolvedExpression Exception
------                                 ------------------ ---------
C:\Program Files\PowerShell\7\pwsh.exe Path

As a result we receive a Microsoft.PowerShell.Commands.PSPropertyExpressionResult object, which consists of the following properties: Result, which contains the value of the specified property, ResolvedExpression, where that property name is located, and Exception, which contains the exception, should one come across during the process of getting the property value.

If all we need is a value of the property specified, then we can get only the Result property of the received object.

$PathProperty.GetValues($ps).Result
C:\Program Files\PowerShell\7\pwsh.exe

Wildcards

We also can use a string with wildcards as an argument to create a new PSPropertyExpression object.

$MemorySize = [pspropertyexpression]::new("*MemorySize64")

The HasWildCardCharacters property of the PSPropertyExpression object now contains True.

$MemorySize.HasWildCardCharacters
True

Now, as in the previous example, let’s call the GetValues method.

$MemorySize.GetValues($ps)
       Result ResolvedExpression         Exception
       ------ ------------------         ---------
        66896 NonpagedSystemMemorySize64
     61038592 PagedMemorySize64
       381112 PagedSystemMemorySize64
     61190144 PeakPagedMemorySize64
2204026884096 PeakVirtualMemorySize64
     61038592 PrivateMemorySize64
2204008886272 VirtualMemorySize64

When create a new PSPropertyExpression object using a string, we also can specify the second parameter of the bool type. Setting it to True will let object constructor know, that the first argument is in its final state and there is no need to try resolving its wildcards. This can be useful, for example, if your objects have properties with wildcard characters.

$WildcardProperty = [pspropertyexpression]::new("Property*Name?", $true)

Parameter Sets

One more feature of the PSPropertyExpression type is that it allows us to specify Property Sets.

For example, the process object has two property sets – PSConfiguration and PSResources.

Get-Process | Get-Member -MemberType PropertySet | Select-Object -Property Name
Name
----
PSConfiguration
PSResources

The first of them – PSConfiguration references following properties.

$ps.PSConfiguration.ReferencedPropertyNames
Name
Id
PriorityClass
FileVersion

Let’s create a PSPropertyExpression object using this property set.

$PSConfiguration = [pspropertyexpression]::new("PSConfiguration")

Now, if we call the GetValues method, then as a result we receive four PSPropertyExpressionResult objects, each of which corresponds to one of the properties referenced by PSConfiguration property set.

$PSConfiguration.GetValues($ps)
Result  ResolvedExpression Exception
------  ------------------ ---------
pwsh    Name
12752   Id
Normal  PriorityClass
7.0.1.0 FileVersion

The GetValues method also have the second overload, which allows us to specify three parameters: target – as in the previous examples, it is the object, values of properties of which we get, expand – setting it to $false prevents resolving property set to its referenced properties, and eatExceptions – setting which to $false will terminate the method execution, should there be any exception throwed. By default, exceptions will only be seen in the Exception property of the PSPropertyExpressionResult object.

$PSConfiguration.GetValues($ps, $false, $false)

As you can guess, there will be no output from the previous command.

It is interesting, that using the PSPropertyExpression object based on the property set, we can get the PSPropertyExpression object for every property that property set references. We can do it by using the ResolveNames method.

$PSConfiguration.ResolveNames($ps)
Script HasWildCardCharacters
------ ---------------------
                       False
                       False
                       False
                       False

And while by default we can’t say, which one corresponds to which property, but we can find this out by using the ToString method.

$PSConfiguration.ResolveNames($ps) | ForEach-Object ToString
Name
Id
PriorityClass
FileVersion

Also, the ResolveNames method have the second overload, that allows us to specify the expand parameter, setting which to $false, as in the case of the GetValues method, prevent the property set resolution to its referenced properties.

$PSConfiguration.ResolveNames($ps, $false)

ScriptBlock

As we already mentioned, besides the strings, the PSPropertyExpression type can accept a scriptblock, that opens even more possibilities for getting and transforming the values of different properties.

$WorkingSet = [pspropertyexpression]::new({$_.WorkingSet64 / 1mb})
$WorkingSet.GetValues($ps)
     Result ResolvedExpression    Exception
     ------ ------------------    ---------
98.66015625 $_.WorkingSet64 / 1mb
$WorkingSet.GetValues($ps).Result
98.66015625

Result

So, what exactly the PSPropertyExpression type brings to us?

Indeed, concerning the case with specifying the existing property

[pspropertyexpression]::new("Path").GetValues($ps)

we definitely can get its value in the usual way.

$ps.Path

But when we specify a string containing wildcards, a property set, or a scriptblock, the PSPropertyExpression object allows us to get the values of the required properties without the necessity to use ForEach-Object or Select-Object cmdlets.

So, instead of

$ps | Select-Object -Property *MemorySize64
$ps | ForEach-Object -Process {$_.WorkingSet64 / 1mb}

we can use aforementioned commands

[pspropertyexpression]::new("*MemorySize64").GetValues($ps)
[pspropertyexpression]::new({$_.WorkingSet64 / 1mb}).GetValues($ps)

and to some extent reduce the amount of required resources and the execution time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s