Prof. Powershell
Different Yet the Same
Compare-Object cmdlet tricks you might not know about.
- By Jeffery Hicks
- 12/08/2009
I know I've mentioned the Compare-Object cmdlet before. It has an alias of diff. The cmdlet is handy when you need to -- what else? -- compare two different types of objects. Typically you want to compare a certain property. Here's a pretty standard example:
PS C:\> $now=get-process
PS C:\> calc
PS C:\> $after=get-process
PS C:\> diff $now $after -Property name
name SideIndicator
---- -------------
calc =>
PS C:\>
The $now object is a snapshot of all the running processes. I then start Calculator and take another snapshot. Using Compare-Object I can identify the process that is running now that wasn't running earlier. The side indicator shows that. But what if I wanted to see more about the process, perhaps because I want to kill any processes running now that weren't running before. I need to tell Compare-Object to pass the entire object through to the pipeline.
PS C:\> diff $now $after -Property name -passthru
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
87 7 5496 11480 71 1.26 1724 calc
If I re-run this and pipe it to Get-Member I can verify that I am in fact getting a Process object. That makes a line this possible.
PS C:\> diff $now $after -Property name -passthru | kill
But let me show you a few other ways to use Compare-Object. I have two folders that I would like to keep sort of synchronized. Any file in FolderA should also be in FolderB. If there are extra files in FolderB, that's ok as long as it also has a copy of everything in FolderA Right now I can tell there is a discrepancy:
PS C:\> $a=dir f:\folderA
PS C:\> $b=dir f:\folderB
PS C:\> $a.count
77
PS C:\> $b.count
76
PS C:\>
For my example I'm only going to synchronize only by file name:
PS C:\> diff $a $b -property Name
name SideIndicator
---- -------------
a.csv =>
b.csv =>
d.csv =>
w.txt <=
w1.txt <=
winrminfo.txt <=
wmidata.txt <=
But all I care about are files in $a that are not in $b. I could try filtering the output on the SideIndicator, but let's pass the objects through but only look at files in FolderA:
PS C:\> diff $a $b -property Name -pass | Where {$_.DirectoryName -eq "F:\folderA"}
Directory: F:\folderA
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 5/14/2009 4:38 PM 863 w.txt
-a--- 5/14/2009 4:42 PM 266 w1.txt
-a--- 3/3/2009 12:41 PM 63538 winrminfo.txt
-a--- 3/21/2009 4:47 PM 96786 wmidata.txt
These files need to be added to FolderB:
PS C:\> diff $a $b -property Name -pass | Where {$_.DirectoryName -eq "F:\folderA"} | copy -dest f:\folderb -passthru
Directory: F:\folderb
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 5/14/2009 4:38 PM 863 w.txt
-a--- 5/14/2009 4:42 PM 266 w1.txt
-a--- 3/3/2009 12:41 PM 63538 winrminfo.txt
-a--- 3/21/2009 4:47 PM 96786 wmidata.txt
All I had to do was pipe the file objects to Copy-Object. I used its -passthru parameter as well so I could see the results. Now all the files in FolderA can also be found in FolderB. It wouldn't take much more to synchronize both ways but I'll leave that exercise to you.
About the Author
Jeffery Hicks (MCSE,MCSA,MCT) is a Microsoft MVP and an IT veteran with almost 20 years of experience, much of it spent as an IT consultant specializing in Windows server technologies. He works today as an independent author, trainer and consultant. Jeff has co-authored or authored several books, courseware, and training videos on administrative scripting and automation. His latest book is Managing Active Directory with Windows PowerShell: TFM (SAPIEN Press 2008). You can follow Jeff at twitter.com/jeffhicks and jdhitsolutions.com/blog.