Renaming the headers with \'label\' and I\'d like to filter on that last one \'SpaceLeft\'. It doesn\'t work correctly though. For example:
Get-WmiObject win
Lee Dailey has provided the crucial pointer in a comment:
To transform data for further programmatic processing, use Select-Object or other data-transformation methods, such as via ForEach-Object.
Only ever use the Format-*
cmdlets to format data for display, as their name suggests.
Format-*
cmdlets such as Format-Table too emit objects, these objects no longer represent data, but formatting instructions for PowerShell's output-formatting system - they serve no other purpose.Therefore, simply replacing Format-Table
with Select-Object
solves your problem:
Get-WmiObject win32_logicaldisk -ComputerName sfuslt167 -Filter "drivetype=3" |
Select-Object -Property deviceID,
@{label='freespace(GB)';expression={$_.freespace / 1GB -as [int]}},
@{label='Size(GB)';expression={$_.size / 1GB -as [int]}},
@{label='SpaceLeft';expression={$_.freespace / $_.size * 100}} |
Where-Object {$_.SpaceLeft -lt 10}
What the two cmdlets do have in common, however, is the ability to accept hashtable-based calculated properties (@{ label = '...'; expression = { ... } }
), as in your question.
As for what actually happened in your attempt:
Here's a simplified example, using Format-Table
:
PS> [pscustomobject] @{ freespace = 100; size = 1000 } |
Format-Table @{label='SpaceLeft'; expression={$_.freespace / $_.size * 100}}
SpaceLeft
---------
10
This looks just fine - and indeed that's the purpose - producing a nice display representation.
In fact, substituting Select-Object
for Format-Table
results in the same display:
PS> [pscustomobject] @{ freespace = 100; size = 1000 } |
Select-Object @{ label='SpaceLeft'; expression={$_.freespace / $_.size * 100} }
SpaceLeft
---------
10
The reason is that when command output goes to the display, PowerShell implicitly, behind the scenes calls an appropriate Format-*
cmdlet, which in this case is Format-Table
.
In other words, the command above is equivalent to the following command:
PS> [pscustomobject] @{ freespace = 100; size = 1000 } |
Select-Object @{label='SpaceLeft'; expression={$_.freespace / $_.size * 100}} |
Format-Table
SpaceLeft
---------
10
For the logic behind what Format-*
cmdlet is chosen when, see this answer.
However, instead of the (implicitly) applied Format-Table
, you could have chosen a different formatting cmdlet, such as Format-List
for list-style display that shows each property on its own line:
PS> [pscustomobject] @{ freespace = 100; size = 1000 } |
Select-Object @{label='SpaceLeft'; expression={$_.freespace / $_.size * 100}} |
Format-List
SpaceLeft : 10
However, when it comes to further processing, Select-Object
and Format-Table
are not created equal - only Select-Object
is suitable:
Let's look at what properties the output object(s) possess, using Get-Member -Type Properties
, first with Select-Object
:
PS> ([pscustomobject] @{ freespace = 100; size = 1000 } |
Select-Object @{label='SpaceLeft'; expression={$_.freespace / $_.size * 100}} |
Get-Member -Type Properties).Name
SpaceLeft
As expected, the output has one property, named SpaceLeft
, and that's what your Where-Object
call can operate on.
Using Format-Table
instead of Select-Object
tells a different story:
PS> ([pscustomobject] @{ freespace = 100; size = 1000 } |
Format-Table @{label='SpaceLeft'; expression={$_.freespace / $_.size * 100}} |
Get-Member -Type Properties).Name
autosizeInfo
ClassId2e4f51ef21dd47e99d3c952918aff9cd
groupingEntry
pageFooterEntry
pageHeaderEntry
shapeInfo
ClassId2e4f51ef21dd47e99d3c952918aff9cd
groupingEntry
shapeInfo
ClassId2e4f51ef21dd47e99d3c952918aff9cd
formatEntryInfo
outOfBand
writeStream
ClassId2e4f51ef21dd47e99d3c952918aff9cd
groupingEntry
ClassId2e4f51ef21dd47e99d3c952918aff9cd
groupingEntry
It doesn't really matter what these in part obscurely named properties specifically represent - all that matters is:
Their sole purpose is to be interpreted by PowerShell's output-formatting system.
The selected / calculated properties passed to Format-Table
are not present as such in the output.
Where-Object
call didn't work as intended: $_.SpaceLeft
referenced a non-existing property, so the expression evaluates to $null
, and $null -lt 10
is always $true
.Irrespective of their input, Format-*
cmdlets output instances of Microsoft.PowerShell.Commands.Internal.Format.* types that represent formatting instructions.