I have a list of file objects, and I want to check whether a given file object appears inside that list. The -Contains
operator is nearly what I'm looking for, but it appears that -Contains
uses a very strict equality test where object references have to be identical. Is there some less strict alternative? I want the $boolean
variable in the code below to return True
the second time as well as the first time.
PS C:\Users\Public\Documents\temp> ls
Directory: C:\Users\Public\Documents\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 14.08.2017 18.33 5 file1.txt
-a---- 14.08.2017 18.33 5 file2.txt
PS C:\Users\Public\Documents\temp> $files1 = Get-ChildItem .
PS C:\Users\Public\Documents\temp> $files2 = Get-ChildItem .
PS C:\Users\Public\Documents\temp> $file = $files1[1]
PS C:\Users\Public\Documents\temp> $boolean = $files1 -Contains $file
PS C:\Users\Public\Documents\temp> $boolean
True
PS C:\Users\Public\Documents\temp> $boolean = $files2 -Contains $file
PS C:\Users\Public\Documents\temp> $boolean
False
PS C:\Users\Public\Documents\temp>
Get-ChildItem
returns objects of the type [System.IO.FileInfo]
.
Get-ChildItem C:\temp\test\2.txt | Get-Member | Select-Object TypeName -Unique
TypeName
--------
System.IO.FileInfo
As PetSerAl mentioned in the comments [System.IO.FileInfo]
does not implement IComparable or IEquatable.
[System.IO.FileInfo].GetInterfaces()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ISerializable
Without these, as you noticed PowerShell will only support reference equality. Lee Holmes' has a great blog post on this.
The solution to this is then to make comparisons on sub-properties that are comparable. You can choose a specific property that would be unique, like Fullname
as Mathias R Jessen mentioned. With the drawback that if the other properties are different, they will not be evaluated.
'a' | Out-File .\file1.txt
$files = Get-ChildItem .
'b' | Out-File .\file1.txt
$file = Get-ChildItem .\file1.txt
$files.fullname -Contains $file.fullname
True
Alternatively, you could use the Compare-Object
cmdlet which will compare all of the properties between the two objects (or the specific properties you choose with -Property
).
Using the -IncludeEqual -ExcludeDifferent
flags of Compare-Object
, we can find all the objects with matching properties. Then when an array is cast to a [bool]
, if it is non-empty it will be $True
and if empty it will be $False
.
'a' | Out-File .\file1.txt
$files = Get-ChildItem .
$file = Get-ChildItem .\file1.txt
[bool](Compare-Object $files $file -IncludeEqual -ExcludeDifferent)
True
'a' | Out-File .\file1.txt
$files = Get-ChildItem .
'b' | Out-File .\file1.txt
$file = Get-ChildItem .\file1.txt
[bool](Compare-Object $files $file -IncludeEqual -ExcludeDifferent)
False
Compare-Object
can be resource intensive, so it's best to use other forms of comparison when possible.