I'm working on a PowerShell script and the Compare-Object
cmdlet is not returning what I expect. I won't put the whole script here, but this is the general idea:
$knownFiles = Import-Csv -Path "C:\knownfiles.csv"
$currentFiles = Get-ChildItem -Path "\\file\share"
$knownFileObject = $knownFiles |
Select-Object -Property Name, Length, LastWriteTimeUTC |
Sort-Object -Property Name
$currentFileObject = $currentFiles |
Select-Object -Property Name, Length, LastWriteTimeUTC |
Sort-Object -Property Name
So now, I have two sorted PSCustomObjects containing the Names, Length and Last Write Time of a set of files. Here's what I can't figure out.
If the objects are identical, Compare-Object
returns nothing, as expected. However, using the example below:
$knownFileObject
looks like this:
Name Length LastWriteTimeUtc ---- ------ ---------------- file1.dat 38988 1/1/2018 8:04:57 AM file2.dat 7182 1/2/2017 8:03:24 AM file4.dat 1026 1/3/2017 8:04:20 AM file5.dat 25137 1/3/2018 8:07:51 AM
$currentFileObject
looks like this:
Name Length LastWriteTimeUtc ---- ------ ---------------- file1.dat 38988 1/1/2018 8:04:57 AM file2.dat 7182 1/2/2017 8:03:24 AM file3.dat 1026 1/2/2018 8:04:30 AM file4.dat 1026 1/3/2017 8:04:20 AM file5.dat 25137 1/3/2018 8:07:51 AM
If I run
Compare-Object -ReferenceObject $knownFileObject -DifferenceObject $currentFileObject
it will return this:
InputObject SideIndicator ----------- ------------- @{Name=file5.dat; Length=25137; LastWriteTimeUtc=1/3/2018 8:07:51 AM} =>
It definitely detects that there is a difference between the two objects, but it always just returns the last row, instead of the actual record that's different. If there are two changed files, it returns the last two rows. If I add a "-property Name"
to the Compare-Object
, it returns the correct file name, but I also need to look for differences in size and write time. I can use Export-Csv
to write both of the objects back out to a file, then run Compare-Object
on the Get-Content
of those files, and that works great, but I don't want to have to write two files to disk, as this job will be running hundreds of times per minute on different folders.
EDIT: Per one of the suggestions, I've also tried Compare-Object ... -Property Name, Length, LastWriteTimeUtc
, and when I do that, it returns every item in both objects, or a 100% mismatch.
What am I doing wrong?
Specify the properties by which you want to compare the objects:
Compare-Object $knownFileObject $currentFileObject -Property Name, Length, LastWriteTimeUtc
You also need to ensure that you're comparing the same thing. The problem with your approach is that the LastWriteTimeUtc
property of FileInfo
objects is a DateTime
object, which has sub-second precision:
PS C:\> Get-Date| Format-List * DisplayHint : DateTime DateTime : Donnerstag, 25. Januar 2018 16:31:07 Date : 25.01.2018 00:00:00 Day : 25 DayOfWeek : Thursday DayOfYear : 25 Hour : 16 Kind : Local Millisecond : 268 ← this here Minute : 31 Month : 1 Second : 7 Ticks : 636524946672682859 TimeOfDay : 16:31:07.2682859 Year : 2018
but the timestamps from your CSV are strings with a precision limited to seconds, thus there'll likely be a miniscule difference between original and stored timestamp.
In your case the best approach would be to store the timestamp in a defined format (long ISO format for instance) and use the same format for the timestamp from the directory listing. Sorting the arrays isn't required, BTW, so you can drop that step.
$saved = Import-Csv 'C:\knownfiles.csv' |
Select-Object Name, Length, LastWriteTimeUtc
$current = Get-ChildItem '\\server\share' |
Select-Object Name, Length,
@{n='LastWriteTimeUtc';e={$_.LastWriteTimeUtc.ToString('s')}}
Compare-Object $saved $current -Property Name, Length, LastWriteTimeUtc