The Compare-Object
cmdlet in PowerShell is a powerful tool for identifying the differences and similarities between two sets of objects. Whether you are comparing files, configurations, or any other data, Compare-Object
provides a flexible way to analyze and understand variations. This article will guide you through using Compare-Object
effectively to Compare Objects in various scenarios.
Syntax
Compare-Object [-ReferenceObject] <psobject> [-DifferenceObject] <psobject> [-SyncWindow <int32>] [-Property <string[]>] [-CaseSensitive] [-Culture <string>] [-ExcludeDifferent] [-IncludeEqual] [-PassThru]
Description
The Compare-Object
cmdlet is designed to compare objects. It takes two sets of objects as input: a reference object set and a difference object set. The cmdlet then analyzes these sets to highlight where they differ and, optionally, where they are the same.
When comparing, Compare-Object
first attempts to find a suitable method for comparing entire objects. If a direct comparison method isn’t available, it defaults to using the ToString() method of each object and compares the resulting strings. For more granular control, you can specify one or more properties to focus the comparison on. In this case, Compare-Object
only compares the values of the specified properties.
The output of Compare-Object
is crucial for understanding the comparison results. It uses a SideIndicator to denote the origin of each object or property value:
<=
: Indicates that the object or property value is present only in the reference object set.=>
: Indicates that the object or property value is present only in the difference object set.==
: (When the -IncludeEqual parameter is used) Indicates that the object or property value is present in both the reference and difference object sets.
It’s important to note that Compare-Object
will generate a terminating error if either the reference or difference objects are null ($null
).
Examples
Example 1 – Basic File Content Comparison
This example demonstrates a fundamental use case: comparing the content of two text files to identify differences. Let’s assume we have two files:
Testfile1.txt
: Contains “dog”, “squirrel”, “bird” (each on a new line).Testfile2.txt
: Contains “cat”, “bird”, “racoon” (each on a new line).
The following PowerShell command compares these files and shows only the differing lines:
$objects = @{
ReferenceObject = (Get-Content -Path C:TestTestfile1.txt)
DifferenceObject = (Get-Content -Path C:TestTestfile2.txt)
}
Compare-Object @objects
Output:
InputObject SideIndicator
----------- -------------
cat =>
racoon =>
dog <=
squirrel <=
bird
In this output, lines unique to Testfile2.txt
(the difference object, indicated by =>
) and lines unique to Testfile1.txt
(the reference object, indicated by <=
) are displayed. Lines common to both files are omitted for clarity in showcasing differences when you compare objects.
Example 2 – Finding Common Content Using ExcludeDifferent
Building upon the previous example, we can use the -ExcludeDifferent parameter to isolate the content that is identical in both files. As of PowerShell 7.1, using -ExcludeDifferent implicitly includes equal items, showing only what’s common.
$objects = @{
ReferenceObject = (Get-Content -Path C:TestTestfile1.txt)
DifferenceObject = (Get-Content -Path C:TestTestfile2.txt)
}
Compare-Object @objects -ExcludeDifferent
Output:
InputObject SideIndicator
----------- -------------
bird ==
This output clearly shows that “bird” is the only line present in both Testfile1.txt
and Testfile2.txt
, demonstrating how to compare objects and find common elements.
Example 3 – Understanding PassThru for Object Type Preservation
By default, Compare-Object
wraps its output in a PSCustomObject, adding properties like InputObject and SideIndicator. However, the -PassThru parameter alters this behavior. It preserves the original object type and instead adds a NoteProperty named SideIndicator to the original objects.
Let’s observe the difference in output types:
$a = $True
Compare-Object -IncludeEqual $a $a
(Compare-Object -IncludeEqual $a $a) | Get-Member
Output (without -PassThru):
InputObject SideIndicator
----------- -------------
True ==
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
InputObject NoteProperty System.Boolean InputObject=True
SideIndicator NoteProperty string SideIndicator===
Now, let’s use -PassThru:
Compare-Object -IncludeEqual $a $a -PassThru
(Compare-Object -IncludeEqual $a $a -PassThru) | Get-Member
Output (with -PassThru):
True
TypeName: System.Boolean
Name MemberType Definition
---- ---------- ----------
CompareTo Method int CompareTo(System.Object obj), int CompareTo(bool value), int IComparable.CompareTo(Syst
Equals Method bool Equals(System.Object obj), bool Equals(bool obj), bool IEquatable[bool].Equals(bool ot
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GetTypeCode Method System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()
ToBoolean Method bool IConvertible.ToBoolean(System.IFormatProvider provider)
ToByte Method byte IConvertible.ToByte(System.IFormatProvider provider)
ToChar Method char IConvertible.ToChar(System.IFormatProvider provider)
ToDateTime Method datetime IConvertible.ToDateTime(System.IFormatProvider provider)
ToDecimal Method decimal IConvertible.ToDecimal(System.IFormatProvider provider)
ToDouble Method double IConvertible.ToDouble(System.IFormatProvider provider)
ToInt16 Method short IConvertible.ToInt16(System.IFormatProvider provider)
ToInt32 Method int IConvertible.ToInt32(System.IFormatProvider provider)
ToInt64 Method long IConvertible.ToInt64(System.IFormatProvider provider)
ToSByte Method sbyte IConvertible.ToSByte(System.IFormatProvider provider)
ToSingle Method float IConvertible.ToSingle(System.IFormatProvider provider)
ToString Method string ToString(), string ToString(System.IFormatProvider provider), string IConvertible.To
ToType Method System.Object IConvertible.ToType(type conversionType, System.IFormatProvider provider)
ToUInt16 Method ushort IConvertible.ToUInt16(System.IFormatProvider provider)
ToUInt32 Method uint IConvertible.ToUInt32(System.IFormatProvider provider)
ToUInt64 Method ulong IConvertible.ToUInt64(System.IFormatProvider provider)
TryFormat Method bool TryFormat(System.Span[char] destination, [ref] int charsWritten)
SideIndicator NoteProperty string SideIndicator===
As shown, without -PassThru, the output is a PSCustomObject. With -PassThru, the output remains a System.Boolean object, but with the added SideIndicator property. This is useful when you need to compare objects and maintain their original type while still knowing the comparison result.
Example 4 – Comparing Simple Objects by Property
Compare-Object
isn’t limited to file content; it can also compare objects based on specific properties. Here, we compare two strings based on their Length
property:
$objects = @{
ReferenceObject = 'abc'
DifferenceObject = 'xyz'
Property = 'Length'
}
Compare-Object @objects -IncludeEqual
Output:
Length SideIndicator
------ -------------
3 ==
Even though the strings themselves are different, their Length
property is the same, and -IncludeEqual
shows this equality when we compare objects by property.
Example 5 – Comparing Complex Objects and Specifying Properties
When dealing with complex objects, such as process objects, Compare-Object
can effectively highlight differences when you specify relevant properties.
First, let’s retrieve two process objects representing different instances of PowerShell:
Get-Process pwsh
$a = Get-Process -Id 11168
$b = Get-Process -Id 17600
$a.ToString()
$b.ToString()
Example Output of Get-Process pwsh (output will vary):
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
101 123.32 139.10 35.81 11168 1 pwsh
89 107.55 66.97 11.44 17600 1 pwsh
Example Output of $a.ToString() and $b.ToString():
System.Diagnostics.Process (pwsh)
System.Diagnostics.Process (pwsh)
If we compare objects $a
and $b
without specifying properties, Compare-Object
might consider them equal because their ToString() representations are the same:
Compare-Object $a $b -IncludeEqual
Output:
InputObject SideIndicator
----------- -------------
System.Diagnostics.Process (pwsh) ==
However, to see the actual differences, we should specify properties like ProcessName
, Id
, and CPU
:
Compare-Object $a $b -Property ProcessName, Id, CPU
Output (output will vary based on process activity):
ProcessName SideIndicator Id CPU
----------- ------------- -- ---
pwsh => 17600 11.4375
pwsh <= 11168 36.203125
By specifying properties, we get a detailed comparison, showing the differences in Id
and CPU
usage, even though the ProcessName
is the same, demonstrating effective object comparison.
Example 6 – Comparing Objects Implementing IComparable
If an object implements the IComparable interface, Compare-Object
leverages this for comparison. It can even handle comparisons between different types by attempting type conversion to the ReferenceObject type.
Consider comparing a string and a TimeSpan object:
Compare-Object ([TimeSpan]"0:0:1") "0:0:1" -IncludeEqual
Output:
InputObject SideIndicator
----------- -------------
00:00:01 ==
In this case, the string "0:0:1"
is converted to a TimeSpan, resulting in equality.
However, reversing the order can lead to a different outcome:
Compare-Object "0:0:1" ([TimeSpan]"0:0:1")
Output:
InputObject SideIndicator
----------- -------------
00:00:01 =>
0:0:1 <=
Here, the TimeSpan is converted to a string for comparison, leading to them being considered different string representations. This highlights the importance of understanding type handling when you compare objects of different types.
Parameters
-CaseSensitive
This parameter makes the comparison case-sensitive. By default, comparisons are case-insensitive.
Compare-Object -ReferenceObject "HELLO" -DifferenceObject "hello" -CaseSensitive
-Culture
Specifies the culture to be used for comparisons, affecting string comparisons and sorting.
Compare-Object -ReferenceObject "Strasse" -DifferenceObject "Straße" -Culture "de-DE"
-DifferenceObject
Specifies the set of objects that will be compared against the ReferenceObject. This is the set from which differences are identified.
Compare-Object -ReferenceObject @(1,2,3) -DifferenceObject @(3,4,5)
-ExcludeDifferent
When used, this parameter ensures that only characteristics that are equal in both object sets are displayed. Differences are discarded, focusing the output on commonalities when you compare objects.
Compare-Object -ReferenceObject @(1,2,3) -DifferenceObject @(2,3,4) -ExcludeDifferent -IncludeEqual
-IncludeEqual
This parameter includes matches (equal objects or property values) in the output, in addition to differences. Equal items are marked with the ==
SideIndicator.
Compare-Object -ReferenceObject @(1,2,3) -DifferenceObject @(2,3,4) -IncludeEqual
-PassThru
As discussed in Example 3, -PassThru returns the original objects with a SideIndicator note property instead of wrapping them in PSCustomObject.
Compare-Object -ReferenceObject @(1,2,3) -DifferenceObject @(2,3,4) -PassThru
-Property
Allows you to specify one or more properties to use for comparison. This is crucial for complex objects where you need to compare objects based on specific attributes. The property can be a simple property name or a calculated property (script block or hash table).
Compare-Object -ReferenceObject (Get-Process pwsh) -DifferenceObject (Get-Process powershell) -Property CPU, WorkingSet
Compare-Object -ReferenceObject @{Name="A"; Value=1} -DifferenceObject @{Name="A"; Value=2} -Property @{Expression={$_.Value}; Label="Value"}
-ReferenceObject
Specifies the baseline set of objects for comparison. Differences are reported relative to this set.
Compare-Object -ReferenceObject @("apple", "banana", "orange") -DifferenceObject @("banana", "grape", "apple")
-SyncWindow
This parameter optimizes comparison performance for large collections by limiting the number of adjacent objects Compare-Object
inspects when searching for a match. The default is [Int32]::MaxValue
(entire collection). Reducing SyncWindow can speed up comparisons but might decrease accuracy in very large, unsorted collections.
Compare-Object -ReferenceObject (1..1000) -DifferenceObject (500..1500) -SyncWindow 100
Inputs
Compare-Object
accepts PSObject input through the pipeline for the DifferenceObject parameter.
Outputs
By default, Compare-Object
produces no output if the ReferenceObject and DifferenceObject are identical.
When differences are found, or when -IncludeEqual is used, Compare-Object
outputs PSCustomObject instances. Each object represents an item from either the ReferenceObject or DifferenceObject sets, along with a SideIndicator property (<=
, =>
, or ==
).
With -PassThru, the cmdlet outputs the original objects with an added SideIndicator note property, preserving the original object type.
Notes
PowerShell aliases for Compare-Object
include compare
and diff
.
When using -PassThru, the console display might not immediately show the SideIndicator property due to default formatting for certain object types. However, the property is indeed added to the object.
Related Links
This enhanced article provides a comprehensive guide on how to use Compare-Object
effectively to compare objects in PowerShell, complete with detailed explanations, varied examples, and SEO optimization for the keyword “compare object”.