26

I have this script to compare two folders.

$firstfolder = Get-ChildItem C:\firstfolder
$secondfolder = Get-ChildItem C:\firstfolder

if ($firstfolder -eq $secondfolder) {
Write-Host "Folders are the same."
} else {
    Write-Host "Error: Doesn't match."
}

As you can see, I compare the same folder. Problem is, that it will never consider, that the arrays are equal. Why?

Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
culter
  • 5,487
  • 15
  • 54
  • 66

3 Answers3

50

In PowerShell, variables that point to arrays are evaluated in expressions by enumerating the contents of the arrays themselves.

For example this expression:

$firstFolder | Get-Member

will return information about the System.IO.DirectoryInfo type, which indeed is the type of the first element in the $firstFolder array. If you want to operate on the array object itself, you have to explicitly tell PowerShell to treat the object as array by using the , operator. The following expression:

,$firstFolder | Get-Member

will expectedly return information about System.Object[].

However, when the -eq operator is used between two array variables, things are a bit different. PowerShell will, in fact, enumerate only the array on the left side and compare each item to the array on the right side as a whole. The result will be an array of matching items or nothing at all when there are no matches. For example:

$a = 1..5
$b = 1..5
$a -eq $b         # returns zero-length array
[bool]($a -eq $b) # returns $false
$a -eq 3          # returns 3

In your particular case, the result will be zero-length array (or $false if casted to a boolean) since the $firstFolder array contains System.IO.DirectoryInfo objects that don't quite match the array in the $secondFolder variable.

What you really want is to compare the contents of both arrays against each other. This is where the Compare-Object cmdlet comes in handy:

Compare-Object $firstFolder $secondFolder -SyncWindow 0

This will return an array of differences between the two arrays or $null when the arrays are equal. More precisely, the resulting array will contain an object for each item that exists only in one array and not the other. The -SyncWindow 0 argument will make the order in which the items appear in the arrays count as a difference.

If all you need is a simple way to tell whether the two arrays are different without going into the details of what is different, you can simply check the length of the array returned by Compare-Object:

$areEqual = @(Compare-Object $firstFolder $secondFolder -SyncWindow 0).Length -eq 0

Notice how I explicitly told PowerShell to always package the results into an array, since Compare-Object may return $null when the arrays don't contain any differences.

4c74356b41
  • 69,186
  • 6
  • 100
  • 141
Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
  • Thank you, Enrico. Did you mean, that it compares the names of the arrays instead of their content? – culter Mar 07 '12 at 09:11
  • @culter The `-eq` operator actually compares the first object in each array. See my updated answer. – Enrico Campidoglio Mar 07 '12 at 10:03
  • Thank you for you exhausting answer, Enrico. It works perfectly. But as you wrote, the -eq operator compare first object of each array, why it returned false when I compared the same folder? – culter Mar 07 '12 at 10:16
  • `$a -eq $b` return not nothing, but zero size array: `($a -eq $b).GetType().FullName; ($a -eq $b).Length` – user4003407 Dec 08 '16 at 15:55
6

For this kind of comparisons you have to use cmdlet compare-object and evaluate if there's differences or not!

CB.
  • 58,865
  • 9
  • 159
  • 159
3

Maybe something like this (with the default -syncwindow, the order doesn't matter, but it's slower for large lists).

function arrayequal($a, $b) {
  -not (compare $a $b)
}

arrayequal $a $b

True

With matching order and faster:

function arrayequal($a, $b) {
  -not (compare $a $b -syncwindow 0)
}

arrayequal $a $b

True
js2010
  • 23,033
  • 6
  • 64
  • 66