Why does this where-object command work?

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}

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. :wink:

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

 

 

 

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}

Another way to look at it:

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

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.

 

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

I usually check the properties like this:

cmdlet | fl *

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

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

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.

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

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

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.

 

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

$_.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 .

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

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. ''.

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