Ctrl-Z for Extracting a ZIP File into an Unintended Location
I accidently extracted an archive file into a folder without creating a sub-folder for those new files to go into. And now they’re sitting alongside a whole bunch of other non-related files. This would typically mean that I would need to start over again, making sure that I dump the contents of the archive file into a sub-folder. After this, I would then need to go back up a folder level and delete all of the files I just extracted prior and make sure that I wouldn’t delete files that were already there. What a headache!
Here’s where automation in the form of PowerShell steps in.
The problem was that I dumped files into the wrong location amidst other non-related files. My solution is to first extract the archive file into it’s intended sub-folder. Secondly, delete ONLY the files that I mistakenly extracted into the parent folder.
Well the first mistake that I made, which landed me in this mess, was using the File Explorer GUI in a situation where PowerShell would have been more succinct and clear.
This time I extracted the file with the PowerShell Expand-Archive cmdlet like so:
PS C:\Users\username\> Expand-Archive C:\example.zip -DestinationPath C:\Example\ExampleSubFolder\
Now I have two folder locations that I can compare. With an emphasis on compare, as that’s the beginning of the cmdlet we’ll use next.
I decided to use the Compare-Object cmdlet as the main workhorse of this scripted solution.
Before I get into the meat of this solution though, there needs to be a little setup first. I want to create a couple variables that will contain all of the files that need to be compared.
Let’s start with C:\Example, where I accidently dumped the files from my archive file.
PS C:\Users\username> $example = ls C:\Example\ -File -Force
Next we’ll move onto the subfolder, which is where we intended our files to go in the first place.
PS C:\Users\username> $exampleSubFolder = ls C:\Example\ExampleSubFolder\ -File -Force
(I should make a couple clarifications in the two above code examples. First, I have some Linux sysadmin experience, so I have a bent towards using the ls alias for the Get-ChildItem cmdlet. You can use dir too. Also this example is just directed towards files, as you can see in the -File parameter. Lastly, I made sure that I captured all files, even system and hidden ones with the -Force parameter.)
We can now compare the files in those two folders and in our comparison, we’re looking for files that are identical. Once we have the identical files, we can remove them from the parent folder, C:\Example, without disrupting the files that were already in C:\Example.
Now onto the good stuff, Compare-Object. The default usage of this cmdlet gives the results of the differences between the two objects being compared. In our case, we want to use it in the exact opposite way, which is having it give us only the files that are identical. This explains the -IncludeEqual and -ExcludeDifferent parameters I use.
PS C:\Users\username> Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent
We’re now on the homestretch. We need to find a way to pass on these results to rm (Linux background taking over again — Remove-Item, which has a surprising amount of aliases del, erase, rd, ri, rm, rmdir), so we can remove the unwanted files from the parent folder, C:\Example.
Based on the table format output that the Compare-Object cmdlet just gave me. I can tell from the header of the files column, that I’ll need to use the InputObject property.
So I go ahead and extract the output of that InputObject property of the Compare-Object cmdlet in the following command.
PS C:\Users\username> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject
Don’t let the parenthesis and dot scare you. My use here of the parenthesis is a grouping expression. This allows me to grab (with the dot) the property of the output of the surrounded command.
We still have more information than we want with the output of the last command so let’s drill down one more level. Using the same idea as the previous command, we’re now going to grab the Name property of the InputObject property.
PS C:\Users\username> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject.name
Ahh beautiful, desired, simple, usable output that can be piped into rm, err Remove-Item.
Here’s the homestretch of the homestretch, I promise.
Go ahead and change to the parent directory where you want to remove the files using the cd command. Officially known as the Set-Location cmdlet in Powershell, cd is an alias that is actually consistent in DOS, Linux, and PowerShell.
PS C:\Users\pmasek\username> cd C:\Example
And just to be sure (parameter -WhatIf), run:
PS C:\Example> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject.name | rm -WhatIf
This allows us to verify that we’re going to delete what we really want to delete.
Then Fire Away with Confidence:
PS C:\Example> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject.name | rm
Hopefully you’ve enjoyed this article and it’s been helpful not only in undoing an accident of extracting files to an unwanted location, but in learning how to automate with PowerShell.
As a reward for getting this far (or if you’re like me, scrolling down because you’re in a pinch and you just need answers), here’s the complete command sequence:
PS C:\Users\username\> Expand-Archive C:\example.zip -DestinationPath C:\Example\ExampleSubFolder\
PS C:\Users\username> $example = ls C:\Example\ -File -Force
PS C:\Users\username> $exampleSubFolder = ls C:\Example\ExampleSubFolder\ -File -Force
PS C:\Users\pmasek\username> cd C:\Example
PS C:\Example> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject.name | rm -WhatIf
Verify that you’re going to delete what you want to delete and proceed.
PS C:\Example> (Compare-Object -ReferenceObject $example -DifferenceObject $exampleSubFolder -IncludeEqual -ExcludeDifferent).inputobject.name | rm
Cmdlet References (Most importantly Get-Help is your friend):
Expand-Archive: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.archive/expand-archive
Get-ChildItem: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.management/get-childitem
Compare-Object: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.utility/compare-object
Set-Location: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.management/set-location
Remove-Item: https://msdn.microsoft.com/en-us/powershell/reference/3.0/microsoft.powershell.management/remove-item