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 is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.