Add a where-object on a table construct?

后端 未结 1 880
死守一世寂寞
死守一世寂寞 2020-11-27 08:08

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         


        
相关标签:
1条回答
  • 2020-11-27 08:47

    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.

      • PowerShell's great evolutionary leap was to send objects rather than text through the pipeline, and while 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.

      • That's why your 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.

    0 讨论(0)
提交回复
热议问题