Why does this where-object command work?

Welcome Forums General PowerShell Q&A Why does this where-object command work?

This topic contains 17 replies, has 7 voices, and was last updated by

 
Participant
2 weeks, 6 days ago.

  • Author
    Posts
  • #124883

    Participant
    Points: 30
    Rank: Member

    I ran across this line the other day that a student wrote in a beginning PowerShell class. I can't figure out how it works since there is no size property for the Win32_QuickFixEngineering object type (which is the object type passed by the Get-Hotfix cmdlet). What is size referring to that makes this command work?

    get-hotfix | where {$_.Description -eq 'Security update' -and $_.size -lt 5MB}

  • #124887

    Participant
    Points: 246
    Helping Hand
    Rank: Participant

    Wow .... funny. Obviously it does not matter what property you specify. At least on my system for example

    get-hotfix | where {$_.Description -eq 'Security update' -and $_.Surname -lt 'RogerRabbit*'}

    produces the same output like your original command line. ūüėČ

  • #124907

    Participant
    Points: 15
    Rank: Member

    Why is nobody using Strict Mode ? Why is it not enabled by default ? (Rethoric questions)

    Use

    Set-StrictMode -Version Latest
    
    Get-Hotfix ... your Where-Object {}

    With Strict Mode, you'll get an error when accessing a property that does not exist (Get-Help Set-StrictMode).

    You'll immediatley see and fix your mistakes, saving lots of time and writing safer code.

    Without it, you get $null for each inexistent property.

    "AnyText".Lenght  is another nice example...

    Another question is why ($null -lt 5MB) and not ($null -gt 5MB)...

    www . powershelltraining . eu

     

     

     

  • #124914

    Participant
    Points: 819
    Helping Hand
    Rank: Major Contributor

    this is because of the type casting and comparison. IMO it compares the length if the $_.size (which is a known existing property hence length is 0) to the '5MB' which has length greater than empty.

    the output will be different if you use -gt instead of -lt.

    below example proves it

    1..5 | Where-Object -FilterScript {$_.f -lt 1}
    1..5 | Where-Object -FilterScript {$_.f -ge 1}
    1..5 | Where-Object -FilterScript {$_.f -ge $null}
    
  • #125001
    js

    Participant
    Points: 316
    Helping Hand
    Rank: Contributor

    Another way to look at it:

    get-hotfix | foreach {$_.size -eq $null}
    True
    True
    True
    True
    True
    True
    True
    True
    True
    True
    True
    True
  • #125030

    Participant
    Points: 30
    Rank: Member

    Thank you. Excellent answers. Indeed it does give different results with -gt. And I was unaware of the "null" response for unknown property values. This has been very excellent help and cleared a lot of things up.

     

  • #125039

    Participant
    Points: 312
    Helping Hand
    Rank: Contributor

    Or just do this, which works with or without Strict mode set.

     
    Set-StrictMode -Version Latest
    
     get-hotfix | where Description -eq 'Security Update'
    
    Source        Description      HotFixID      InstalledBy          InstalledOn              
    ------        -----------      --------      -----------          -----------              
    Lab01          Security Update  KB4457146     NT AUTHORITY\SYSTEM  9/12/2018 12:00:00 AM    
    Lab01          Security Update  KB4465663     NT AUTHORITY\SYSTEM  11/14/2018 12:00:00 AM   
    Lab01          Security Update  KB4467694     NT AUTHORITY\SYSTEM  11/14/2018 12:00:00 AM   
    Lab01          Security Update  KB4467702     NT AUTHORITY\SYSTEM  11/14/2018 12:00:00 AM 
    

    Also the reason for your '-and' failure, is that there is no property called 'size', so combining the will fail of course.

     (get-hotfix)[0] | Get-Member | ft -a
    
       TypeName: System.Management.ManagementObject#root\cimv2\Win32_QuickFixEngineering
    
    Name                MemberType     Definition                                                                                                                
    ----                ----------     ----------                                                                                                                
    PSComputerName      AliasProperty  PSComputerName = __SERVER                                                                                                 
    Caption             Property       string Caption {get;set;}                                                                                                 
    CSName              Property       string CSName {get;set;}                                                                                                  
    Description         Property       string Description {get;set;}                                                                                             
    FixComments         Property       string FixComments {get;set;}                                                                                             
    HotFixID            Property       string HotFixID {get;set;}                                                                                                
    InstallDate         Property       string InstallDate {get;set;}                                                                                             
    InstalledBy         Property       string InstalledBy {get;set;}                                                                                             
    Name                Property       string Name {get;set;}                                                                                                    
    ServicePackInEffect Property       string ServicePackInEffect {get;set;}                                                                                     
    Status              Property       string Status {get;set;}                                                                                                  
    __CLASS             Property       string __CLASS {get;set;}                                                                                                 
    __DERIVATION        Property       string[] __DERIVATION {get;set;}                                                                                          
    __DYNASTY           Property       string __DYNASTY {get;set;}                                                                                               
    __GENUS             Property       int __GENUS {get;set;}                                                                                                    
    __NAMESPACE         Property       string __NAMESPACE {get;set;}                                                                                             
    __PATH              Property       string __PATH {get;set;}                                                                                                  
    __PROPERTY_COUNT    Property       int __PROPERTY_COUNT {get;set;}                                                                                           
    __RELPATH           Property       string __RELPATH {get;set;}                                                                                               
    __SERVER            Property       string __SERVER {get;set;}                                                                                                
    __SUPERCLASS        Property       string __SUPERCLASS {get;set;}                                                                                            
    PSStatus            PropertySet    PSStatus {__PATH, Status}                                                                                                 
    ConvertFromDateTime ScriptMethod   System.Object ConvertFromDateTime();                                                                                      
    ConvertToDateTime   ScriptMethod   System.Object ConvertToDateTime();                                                                                        
    InstalledOn         ScriptProperty System.Object InstalledOn {get=if ([environment]::osversion.version.build -ge 7000)...                                    
    

    Even if you look at the extended properties

    (get-hotfix)[0] | Select * | Format-List -Force
    
  • #125042
    js

    Participant
    Points: 316
    Helping Hand
    Rank: Contributor

    I usually check the properties like this:

    cmdlet | fl *
    

    Just "| fl" might use a format file and not show everything.

  • #125045

    Participant
    Points: 312
    Helping Hand
    Rank: Contributor

    But this will, including some not show any other way, still, no size property, which contributes to the OP's error.

     
    (get-hotfix)[0] | Get-Member -Force
    
    Name                    MemberType
    ----                    ----------
    PSComputerName       AliasProperty
    pstypenames           CodeProperty
    psadapted                MemberSet
    psbase                   MemberSet
    psextended               MemberSet
    psobject                 MemberSet
    PSStandardMembers        MemberSet
    Caption                   Property
    CSName                    Property
    Description               Property
    FixComments               Property
    HotFixID                  Property
    InstallDate               Property
    InstalledBy               Property
    Name                      Property
    ServicePackInEffect       Property
    Status                    Property
    __CLASS                   Property
    __DERIVATION              Property
    __DYNASTY                 Property
    __GENUS                   Property
    __NAMESPACE               Property
    __PATH                    Property
    __PROPERTY_COUNT          Property
    __RELPATH                 Property
    __SERVER                  Property
    __SUPERCLASS              Property
    PSStatus               PropertySet
    ConvertFromDateTime   ScriptMethod
    ConvertToDateTime     ScriptMethod
    InstalledOn         ScriptProperty
    
  • #125387

    Participant
    Points: -11
    Rank: Member

    this is working because, if some property is not present for an object  Powershell will create a new custom property. have a look at the below code.

     

    PS C:\windows\system32> get-hotfix | select size

    size
    —-

     

    It won't give any error instead it will create a new property called size. As size does not have any value it will hold $null.Now if you compare by using (-lt)  it will always return true as ($null -lt any value) is true. Have a look at the  below code,

     

    PS C:\windows\system32> get-hotfix | for each {$_.size -lt 5}
    True
    True
    True
    True
    True
    True
    True
    True
    True

     

    now if you change the compare operator to (-gt )it will always give false. As $null -gt any value is false.

  • #125748

    Participant
    Points: 819
    Helping Hand
    Rank: Major Contributor

    Foreach doesn't behaves like Select-Object. Here comparison happens with the length of the non-existing property, hence its true.

  • #125798
    js

    Participant
    Points: 316
    Helping Hand
    Rank: Contributor

    Hmm, but $null isn't equal to 0?

    PS C:\users\admin> $null -lt 1
    True
    PS C:\users\admin> $null -lt -1
    False
    PS C:\users\admin> $null -le 0
    True
    PS C:\users\admin> $null -eq 0
    False
    PS C:\users\ccfadmin> $null -lt 0
    True
    
  • #125807

    Participant
    Points: 15
    Rank: Member

    Well, $null is $null... it has no type. It is not equal to 0 by itself.

    But sometimes, variables in PowerShell can be converted (Cast) to another type.  $Null can be converted to [int], and it has to have a valid value.  Both

    [int]$Null
    $Null -AS [int]

    return 0, by convention.

     

  • #125808
    js

    Participant
    Points: 316
    Helping Hand
    Rank: Contributor

    Hmm, $null is in there somewhere close to 0!

    PS C:\users\admin> $null -gt -.00000000000000000000000000000000000001
    True
    PS C:\users\admin> $null -lt 0
    True
    
  • #125855

    Participant
    Points: 819
    Helping Hand
    Rank: Major Contributor

    $_.size -lt 5 converts $null to int and [int]$null is 0, hence it returns true

    $_.size -lt '5' converts $null to string and [string]$null length is 0, hence it returns true .

  • #125858
    js

    Participant
    Points: 316
    Helping Hand
    Rank: Contributor

    Usually the type is dictated by the left element in the comparison. $null seems to be a special case, since it has no type.

    PS C:\Users\admin> '10' -gt 9
    False
    PS C:\Users\admin> 10 -gt '9'
    True
    PS C:\Users\admin> [datetime]::today -eq '11/20'
    True
    
  • #125870

    Participant
    Points: 819
    Helping Hand
    Rank: Major Contributor

    yes it is a special case, just had a chat with Bruce Payette and PFB his comments.

    Bruce Payette:

    $null in a numeric context (like comparing to a number) is converted to 0. And since 0 is less than 1,
    $null is special-cased by the runtime. It doesn't actually get converted, but is treated as a value less than 0 but greater that the smallest negative number. Likewise in a string context, it is treated as being 'smaller' than the smallest possible string i.e. ".

  • #126018

    Participant
    Points: 30
    Rank: Member

    This simple question has brought out a lot if interesting nuances and taught me, and hopefully others, a lot. I appreciate all the feedback.

     

     

You must be logged in to reply to this topic.