Liberator17
Liberator17

Reputation: 31

Powershell Script to delete files not in a list with progress bar

I have a folder with .jpg files in it. I associate these with products in an access database. One of the sources for products provides these .jpg files but they do not allow you to easily download only the pictures that you currently use. Therefore I have found a PowerShell Script to delete the files that I do not need.

$exclusions = Get-Content C:\Users\office\Desktop\ExcludedPhotos.txt
dir -rec M:\PhotoDirectory\PhotoFolder | Where-Object {$exclusions -notcontains $_.name } | Remove-Item

Credit to @x0n Powershell script to delete files not specified in a list

And it works great! but the problem is it takes forever and I have over 180,000 items to search through and delete. So I wanted to make a progress bar that would let me know how far I had gone through the process.

So after a little bit of searching I found an article called "using the progress bar"

The problem is I don't know how to mash the two together, However I have made an attempt here:

$exclusions = Get-Content C:\Users\office\Desktop\ExcludedPhotos.txt 
  1..100 | foreach-object {
    Write-Progress -Activity "Deleting Files" -Status "$_ %" -Id 1 -PercentComplete $_ -CurrentOperation "Deleting File $_"
       dir -rec M:\PhotoDirectory\PhotoFolder | Where-Object {$exclusions -notcontains $_.name } | Remove-Item
        }

However that seems to take even longer than the original script did, I don't know exactly how it is working, and I am testing it when I only need to remove 10-15 files.

It is likely there is something really basic I am missing But I would really appreciate some help understanding this.

Here I have added a screenshot:

PowerShell Console

Upvotes: 3

Views: 3330

Answers (2)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174485

However that seems to take even longer than the original script did

That's because you're attempting to enumerate, filter and delete the files 100 times - which is obviously unnecessary.

What you want to do is calculate how far in the process of deleting the files you are, and then use that as a basis for the percentage you pass to Write-Progress. The easiest way to keep count is probably to use a regular for loop:

Note: these first two examples are very generic, go to the bottom for an example that takes your volume (~180.000 files) into account.

# Grab all the files you need to delete
$exclusions = Get-Content C:\Users\office\Desktop\ExcludedPhotos.txt
$filesToDelete = Get-ChildItem M:\PhotoDirectory\PhotoFolder -Recurse | Where-Object {$exclusions -notcontains $_.Name }

for($i = 0; $i -lt $filesToDelete.Count; $i++){
    # calculate progress percentage
    $percentage = ($i + 1) / $filesToDelete.Count * 100
    Write-Progress -Activity "Deleting Files" -Status "Deleting File #$($i+1)/$($filesToDelete.Count)" -PercentComplete $percentage
    # delete file
    $filesToDelete[$i] |Remove-Item
}
# All done
Write-Progress -Activity "Deleting Files" -Completed

Another characteristic you might want to use to indicate a percentage is the relative volume (total number of bytes) removed. We can do this by simply keeping track of how many bytes we need to remove total, and how many we've removed so far:

$exclusions = Get-Content C:\Users\office\Desktop\ExcludedPhotos.txt
$filesToDelete = Get-ChildItem M:\PhotoDirectory\PhotoFolder -Recurse | Where-Object {$exclusions -notcontains $_.Name }
$TotalSize = ($filesToDelete |Measure-Object -Property Length -Sum).Sum
$BytesRemoved = 0

foreach($file in $filesToDelete){
    $percentage = $BytesRemoved / $TotalSize * 100
    Write-Progress -Activity "Deleting Files" -Status "Deleted $BytesRemoved/$TotalSize bytes" -PercentComplete $percentage
    $file |Remove-Item
    $BytesRemoved += $file.Length
}
Write-Progress -Activity "Deleting Files" -Completed

As @MatthewWetmore points out, invoking Write-Progress every time you delete a file will of course incur some overhead. And with 180.000 files, you're probably not that interested in having the UI update when the progress goes from 3.56325% to 3.56331%

What you could do is use the for loop to count in increments of 1% of the entire set of items, and then remove a whole range of files on each iteration:

[int]$hundredthStep = $filesToDelete.Count / 100
for($i = 0; $i -lt $filesToDelete.Count; $i += $hundredthStep){
    # calculate progress percentage
    $percentage = ($i + 1) / $filesToDelete.Count * 100
    Write-Progress -Activity "Deleting Files" -Status "Deleting File up to #$($i+1)/$($filesToDelete.Count)" -PercentComplete $percentage
    # delete file
    $filesToDelete[$i..($i + $hundredthStep - 1)] |Remove-Item
}
# All done
Write-Progress -Activity "Deleting Files" -Completed

Upvotes: 6

Mark Wragg
Mark Wragg

Reputation: 23355

You want to do something like this (beware this is untested so i've put -whatif on the remove cmdlet which you should remove when you're happy it's working correctly):

$exclusions = Get-Content C:\Users\office\Desktop\ExcludedPhotos.txt 
$files = dir -rec M:\PhotoDirectory\PhotoFolder | Where-Object {$exclusions -notcontains $_.name } 

$files | foreach-object {
    $num += 1    
    Write-Progress -Activity "Deleting Files" -Status "$_ %" -Id 1 -PercentComplete (($num / $files.count) * 100)  -CurrentOperation "Deleting File $($_.name)"
    $_ | Remove-Item -WhatIf
}

Upvotes: 2

Related Questions