Compare folder contents with another folder and delete - powershell

I have 1000 files in a folder and similar named files in another folder. The objective is to have identical filenames in both folders but keep the file extension.
I would like to run a script to compare each folder's content (except their file extension) and if they are not in on folder If there is a file called BILL in folder1 but not in folder2 it deletes the file in one of the folders.
Example:
C:\TempFolder1\RandomFile1
C:\TempFolder2
If RandomFile1 does not exist in TempFolder2 it deletes it from TempFolder1 and vice versa.

Here you go... this script assumes you aren't looking recursively through subfolders, ignoring directories as well underneath either parent folder... it works by pulling the file list, then comparing each folders child files' BaseName with the list of BaseNames from the other, then removing the unique ones:
$folder1 = "C:\TempFolder1"
$folder2 = "C:\TempFolder2"
$files1 = Get-ChildItem $folder1 | Where-Object {$_.PsIsContainer -eq $false}
$files2 = Get-ChildItem $folder2 | Where-Object {$_.PsIsContainer -eq $false}
# Remove unique file baseNames from $folder1 that don't exist in $folder2
$files1 | Where-Object {$files2.BaseName -notcontains $_.BaseName} | Remove-Item -Force
# Remove unique file baseNames from $folder2 that don't exist in $folder1
$files2 | Where-Object {$files1.BaseName -notcontains $_.BaseName} | Remove-Item -Force

Related

Powershell: Find Folders and Run Command in Those Folders

so trying to find a way to combine a couple of things the Stack Overflow crowd has helped me do in the past. So I know how to find folders with a specific name and move them where I want them to go:
$source_regex = [regex]::escape($sourceDir)
(gci $sourceDir -recurse | where {-not ($_.psiscontainer)} | select -expand fullname) -match "\\$search\\" |
foreach {
$file_dest = ($_ | split-path -parent) -replace $source_regex,$targetDir
if (-not (test-path $file_dest)){mkdir $file_dest}
move-item $_ -Destination $file_dest -force -verbose
}
And I also know how to find and delete files of a specific file extension within a preset directory:
Get-ChildItem $source -Include $searchfile -Recurse -Force | foreach{ "Removing file $($_.FullName)"; Remove-Item -force -recurse $_}
What I'm trying to do now is combine the two. Basically, I'm looking for a way to tell Powershell:
"Look for all folders named 'Draft Materials.' When you find a folder with that name, get its full path ($source), then run a command to delete files of a given file extension ($searchfile) from that folder."
What I'm trying to do is create a script I can use to clean up an archive drive when and if space starts to get tight. The idea is that as I develop things, a lot of times I go through a ton of incremental non-final drafts (hence folder name "Draft Materials"), and I want to get rid of the exported products (the PDFs, the BMPs, the AVIs, the MOVs, atc.) and just leave the master files that created them (the INDDs, the PRPROJs, the AEPs, etc.) so I can reconstruct them down the line if I ever need to. I can tell the script what drive and folder to search (and I'd assign that to a variable since the backup location may change and I'd like to just change it once), but I need help with the rest.
I'm stuck because I'm not quite sure how to combine the two pieces of code that I have to get Powershell to do this.
If what you want is to
"Look for all folders named 'Draft Materials.' When you find a folder with that name, get its full path ($source), then run a command to delete files of a given file extension ($searchfile) from that folder."
then you could do something like:
$rootPath = 'X:\Path\To\Start\Searching\From' # the starting point for the search
$searchFolder = 'Draft Materials' # the folder name to search for
$deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV' # an array of file patterns to delete
# get a list of all folders called 'Draft Materials'
Get-ChildItem -Path $rootPath -Directory -Filter $searchFolder -Recurse | ForEach-Object {
# inside each of these folders, get the files you want to delete and remove them
Get-ChildItem -Path $_.FullName -File -Recurse -Include $deleteThese |
Remove-Item -WhatIf
}
Or use Get-ChildItem only once, having it search for files. Then test if their fullnames contain the folder called 'Draft Materials'
$rootPath = 'X:\Path\To\Start\Searching\From'
$searchFolder = 'Draft Materials'
$deleteThese = '*.PDF', '*.BMP', '*.AVI', '*.MOV'
# get a list of all files with extensions from the $deleteThese array
Get-ChildItem -Path $rootPath -File -Recurse -Include $deleteThese |
# if in their full path names the folder 'Draft Materials' is present, delete them
Where-Object { $_.FullName -match "\\$searchFolder\\" } |
Remove-Item -WhatIf
In both cases I have added safety switch -WhatIf so when you run this, nothing gets deleted and in the console is written what would happen.
If that info shows the correct files are being removed, take off (or comment out) -Whatif and run the code again.

Powershell List Excel Files and Copy

I apologize for the naivety of this post, please forgive my newness.
I have approximately 20,000 network files to filter through and copy certain ones to a local drive.
File List Requirements:
Excel files of various type (.xls, .xlsx, .xlsm)
Only files modified after 4/1/2022
Only files that contain "2022" in the filename
If the file meets those requirements then:
Copy the file to a local folder (original folder path structure doesn't matter, all files can go in one folder)
Output the original path and filename to a txt file, along with the lastwritedate
I have created the following code, which successfully obtains all excel files and creates the filename list
Get-ChildItem "D:\network_folder\" -Filter *.xls -Recurse | Select-Object -Property FullName, LastWriteTime |
Export-Csv -Path "C:\local_folder\file_list.csv" -Force -NoTypeInformation
However I cannot figure out the following issues:
how and where to filter for the lastwritetime
how and where to filter for the "2022" in the name
how and where to copy the files to the local folder
right now I'm just putting this all in the command line, do I need to make some file to run this process?
Thank you for any assistance you can provide!
I guess you want something like this.
It searches for files in the source folder with 2022 in the name and having .xls (or anything following xls) as extension.
It then loops over these items, creates the subfolder structure where they were found in the destination folder, copies the files and finally writes out a CSV file with information of the original file.
$sourcePath = 'D:\network_folder'
$destination = 'D:\dest_folder'
$refDate = [datetime]::new(2022,4,2) # --> next day date as of midnight
Get-ChildItem -Path $sourcePath -Filter '*2022*.xls*' -File -Recurse |
Where-Object {$_.LastWriteTime -ge $refDate} | ForEach-Object {
# create the destination folder if it does not already exist
$target = Join-Path -Path $destination -ChildPath $_.DirectoryName.Substring($sourcePath.Length)
$null = New-Item -Path $target -ItemType Directory -Force
# copy the file
$_ | Copy-Item -Destination $target
# output the wanted properties from the original file
$_ | Select-Object Name, FullName, LastWriteTime
} | Export-Csv -Path "C:\local_folder\file_list.csv" -Force -NoTypeInformation

How to find all folders which has folder named "Intro" AND filetype *.mp4

The closest I got was using powershell Get-ChildItem given multiple -Filters
Get-ChildItem -Include "Intro", "*.mp4" -Recurse
I think, -Include with multiple params work as OR operator. It gives folders with either "Intro" folder OR "*.mp4" files.
But I need AND condition. Folder must contain a folder named "Intro" and "*.mp4" files.
I need folders structured following -
E:.
└───test1.mp4
└───test2.mp4
└───test3.mp4
└───test4.mp4
└───test5.mp4
└───test6.mp4
└───Intro
Update 1
I am searching for folders which meet two condition.
It must have a subfolder named Intro AND
It must have *.mp4 files.
The answer would look something like the following I guess.
Get-ChildItem -Directory -Recurse {HasSubFolderNamedIntro && HasMP4Files}
Just wanted to add another Method (one-liner) to get the folder which contains mp4 files and a folder Intro (probs to #HenrikStanleyMortensen for most of it):
(Get-ChildItem -include "*.mp4" -Recurse -File).DirectoryName | Select-Object -Unique | Where-Object {(Get-ChildItem $_ -Recurse -Include 'Intro' -Directory)}
Do you need the command to only return the file objects or do you also want the folder objects?
If just the files I would do it like this:
Get-ChildItem -include "*.mp4" -Recurse -File | Where-Object {$_.Directory.Name -match 'Intro'}
So we use the include to find the mp4 files and reduce the amount of objects we pipe.
Then we pipe it to Where-Object and look for the property with the name of the folder and says we want it to contain the word "intro". If the folder needs to be called Intro exactly and not just contain it you can change the -match to -eq
Edit
To get the directories then we could do it like this:
(Get-ChildItem -include "*.mp4" -Recurse | Where-Object {$_.Directory.Name -match 'Intro'}).DirectoryName | Select-Object -Unique
Now we say that all the files that we found that matches our search, we want to see the full directory path of those files.
To only get one match per directory, so if we have 1 directory that matches with multiple mp4 files, and we don't want to see that same directory in our output one time per file, we can pipe the result into Select-Object -Uniqueto only see each directory once.
Edit 2
After clarification from OP.
To find a folder that contains both mp4 files and a subfolder called intro I don't think we can do that only from the Get-ChildItem command in any way I know of, so we can loop through each folder like this:
$Files = (Get-ChildItem -include "*.mp4" -Recurse -File).DirectoryName | Select-Object -Unique
foreach($File in $Files) {
Get-ChildItem -Path $File.DirectoryName -Recurse -Include 'Intro' -Directory
}
We Pipe to the Select-Object -Unique to make sure that folders with multiple mp4 files are not looped through more than once thus giving us an output with the same intro folder multiple times.

PowerShell remove last sub folder

Is there a way how to remove only last empty folder with PowerShell?
Example: I have a folder structure
..-mainFolder
...................-subFolder1
........................................- a
........................................- b
........................................- c
..................-subFolder2
........................................- a
........................................- b
Every night with robocopy i copy everything to another server and afterword i should delete all last sub folders (a,b,c, etc..).
With /MUVE it removes "subFolder1" & "subFolder2" but they should stay there
(if i remove folders "a", "b", "c" the "subFolder1" is empty too so i cant delete all empty folders.)
I cant use /FX and i don't know the name of folders just root directory path "C:\SharedFolders\". and i know that the folders that should be removed is in 3rd level.
You could use the Get-ChildItem cmdlet with the -Directory switch to retrieve all folders, filter the empty folders using the Test-Path cmdlet and finally delete the folders using Remove-Item:
Get-ChildItem 'C:\SharedFolders' -Directory -Recurse |
where { -not (Test-Path (Join-Path $_.FullName '*')) } |
Remove-Item
This will remove only the last empty folders, result:
..-mainFolder
...................-subFolder1
..................-subFolder2
You can use Get-ChildItem -Recurse to retrieve all folders, then call the GetFiles() and GetDirectories() methods on the directory objects to determine if they are empty:
$EmptyDirs = Get-ChildItem C:\path\to\mailFolder -Directory -Recurse | Where {-not $_.GetFiles() -and -not $_.GetDirectories()}
# and then remove those
$EmptyDirs | Remove-Item

How can Powershell copy an entire folder structure but exclude one folder and its contents

This seems like a simple operation but I can't figure out how to get Powershell to copy an entire folder structure from one location to another but exclude one folder (called 'connections') and its contents.
I've tried combining Copy-Item and Get-ChildItem like so
cpi (gci folder1 -Exclude connections) folder2 -recurse
but it seems the -recurse parameter overwrites the -exclude parameter and the connections folder and its contents are copied. Without -recurse the contents of the folders I do want copied are ignored.
I'm not sure why that isn't working, it seems to behave correctly on my machine.
You could always pipe to Copy-Item:
Get-ChildItem folder1 | where { !(($_ -is [System.IO.DirectoryInfo]) -and ($_.Name -eq "connections")) } | Copy-Item -Destination folder2 -Recurse
The advantage of this is that you can just get PowerShell to print out the output after:
Get-ChildItem folder1 | where { !(($_ -is [System.IO.DirectoryInfo]) -and ($_.Name -eq "connections")) }
That way you can check exactly what is getting copied (i.e. is the "connections" folder missing?)