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.