1

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 $booleanvariable 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>
Magnus
  • 589
  • 8
  • 26
  • What version of powershell are you using? – Mathias R. Jessen Aug 14 '17 at 16:49
  • Either `$files2 = $files1.Clone()` or use `Where-Object` – wOxxOm Aug 14 '17 at 16:51
  • 1
    `[bool](Compare-Object $files2 $file -IncludeEqual -ExcludeDifferent)` – BenH Aug 14 '17 at 16:51
  • `-Contains` use standard PowerShell equality testing: `([bigint]1) -contains '0001'`. But `FileInfo` class does not describe how to compare value. – user4003407 Aug 14 '17 at 17:06
  • `$Files2.FullName -contains $file.FullName` – Mathias R. Jessen Aug 14 '17 at 17:09
  • What is it you really want to do? Find file with same contents, same names, or other? – Kory Gill Aug 14 '17 at 18:57
  • @KoryGill I'm making a script that monitors a directory for new file additions and automatically executes any new file that appears. – Magnus Aug 14 '17 at 21:40
  • 1
    So do you need to handle delete, rename, move, locked files, etc. to ensure that you catch every situation there is? Reason I asked is that in such case you may need to consider hashing or the like. As for what you are doing, RoboCopy actually can provide a lot of info that may do a lot of the "heavy lifting" for you. That's another topic altogether... – Kory Gill Aug 15 '17 at 18:43
  • It's not that critical, but I still appreciate your suggestion. My script is designed to emulate an end-user that uncritically executes any new file that is uploaded to an FTP-server. Both this client machine and the FTP-server is in an isolated lab outside of production and will only be handled by people who know what they're doing – Magnus Aug 15 '17 at 20:20

1 Answers1

1

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.

BenH
  • 9,766
  • 1
  • 22
  • 35