Данный сценарий находит в указанных папках файлы с одинаковым размером и на основании MD5-хэша отбирает из них файлы с одинаковым содержимым
function Find-EqualFile { <# .SYNOPSIS Командлет предназначен для поиска одинаковых файлов .DESCRIPTION Командлет находит в указанных папках файлы с одинаковым размером и на основании MD5-хэша отбирает из них файлы с одинаковым содержимым .PARAMETER Folders Папки, в которых необходимо найти одинаковые файлы .PARAMETER OutputCSV Путь к CSV-файлу со списком одинаковых файлов .INPUTS - .OUTPUTS - .NOTES Версия 1.3 (c) 2019 Александр Галков, [email protected] .EXAMPLE Find-EqualFile -Folders "c:\folder1","c:\folder2" -OutputCSV "c:\output.csv" .LINK www.galkov.pro/powershell_script_for_finding_equal_files #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true)][string[]]$Folders, [Parameter(Mandatory=$true)][string]$OutputCSV ) $format = "yyyy-MM-dd HH:mm:ss" #составляем список всех файлов Write-Host ("{0} Составляем список всех файлов..." -f ([DateTime]::Now).ToString($format)) $all_files = @() foreach ($folder in $Folders) { $all_files += Get-ChildItem -LiteralPath $folder -File -Recurse -Force } Write-Host ("{0} Обнаружено файлов: {1}" -f ([DateTime]::Now).ToString($format), $all_files.Length) #составляем список файлов с одинаковым размером Write-Host ("{0} Составляем список файлов с одинаковым размером..." -f ([DateTime]::Now).ToString($format)) $eq_size_groups = @{} for ($i=0; $i -lt $all_files.Length; $i++) { $size = $all_files[$i].Length if (!$eq_size_groups.ContainsKey($size)) { $eq_size_groups.Add($size,@()) } $eq_size_groups[$size] += $all_files[$i] } $eq_size_file_count = 0 $eq_size_group_count = 0 foreach ($eq_size_files in $eq_size_groups.Values) { if ($eq_size_files.Length -gt 1) { $eq_size_file_count += $eq_size_files.Length $eq_size_group_count++ } } Write-Host ("{0} Обнаружено групп файлов с одинаковым размером: {1}, в них файлов: {2}" -f ([DateTime]::Now).ToString($format), $eq_size_group_count, $eq_size_file_count) #вычисляем хэш Write-Host ("{0} Вычисляем хэш..." -f ([DateTime]::Now).ToString($format)) $eq_hash_groups = @{} $proc_file_count = 0 foreach ($eq_size_files in $eq_size_groups.Values) { if ($eq_size_files.Length -gt 1) { for ($i=0; $i -lt $eq_size_files.Length; $i++) { $hash = Get-FileHash -LiteralPath $eq_size_files[$i].FullName -Algorithm MD5 if (!$eq_hash_groups.ContainsKey($hash.Hash)) { $eq_hash_groups.Add($hash.Hash,@()) } $eq_hash_groups[$hash.Hash] += $eq_size_files[$i] if ($proc_file_count%[Math]::Ceiling($eq_size_file_count/10) -eq 0 -or $proc_file_count -eq $eq_size_file_count-1) { write-host ("{0} Обработано: {1} %" -f ([DateTime]::Now).ToString($format), [Math]::Round(($proc_file_count+1)/$eq_size_file_count*100)) } $proc_file_count++ } } } $eq_hash_file_count = 0 $eq_hash_group_count = 0 foreach ($eq_hash_files in $eq_hash_groups.Values) { if ($eq_hash_files.Length -gt 1) { $eq_hash_file_count += $eq_hash_files.Length $eq_hash_group_count++ } } Write-Host ("{0} Обнаружено групп файлов с одинаковым хэшем: {1}, в них файлов: {2}" -f ([DateTime]::Now).ToString($format), $eq_hash_group_count, $eq_hash_file_count) #сохраняем результат Write-Host ("{0} Сохраняем результат в файл {1}" -f ([DateTime]::Now).ToString($format), $OutputCSV) $file_instances = @() foreach ($hash in $eq_hash_groups.Keys) { $eq_hash_files = $eq_hash_groups[$hash] if ($eq_hash_files.Length -gt 1) { $eq_hash_files = $eq_hash_files | Sort -Property Fullname for ($i=0; $i -lt $eq_hash_files.Length; $i++) { $file_instance = New-Object -TypeName PSObject Add-Member -InputObject $file_instance -MemberType NoteProperty -Name ID -Value 0 Add-Member -InputObject $file_instance -MemberType NoteProperty -Name Fullname -Value $eq_hash_files[$i].Fullname Add-Member -InputObject $file_instance -MemberType NoteProperty -Name Instance -Value ($i+1) Add-Member -InputObject $file_instance -MemberType NoteProperty -Name Hash -Value $hash $file_instances += $file_instance } } } $file_instances = $file_instances | Sort -Property Instance,Fullname for ($i=0; $i -lt $file_instances.Length; $i++) { $file_instances[$i].ID = $i } $file_instances | Export-Csv -LiteralPath $OutputCSV -Encoding UTF8 -NoTypeInformation }
После этого можно удалить лишние экземпляры файлов, используя следующий сценарий
function Remove-ExcessFile { <# .SYNOPSIS Командлет предназначен для удаления лишних экземпляров файлов .DESCRIPTION - .PARAMETER InputCSV CSV-файл, полученный в результате выполнения командлета Find-EqualFile .PARAMETER IDs ID файлов, которые нужно удалить .INPUTS - .OUTPUTS - .NOTES Версия 1.0 (c) 2019 Александр Галков, [email protected] .EXAMPLE Remove-ExcessFile -InputCSV "с:\input.csv" -IDs "5-25,30,50-75,100" .LINK www.galkov.pro/powershell_script_for_finding_equal_files #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true)][string]$InputCSV, [Parameter(Mandatory=$true)][string]$IDs ) $all_files = Import-Csv -LiteralPath $InputCSV -Encoding UTF8 $intervals = @() while ($IDs -ne "") { if ($IDs -match "(.*),(.*)") { $IDs = $Matches[1] $id = $Matches[2] } else { $id = $IDs $IDs = "" } $interval = New-Object -TypeName PSObject Add-Member -InputObject $interval -MemberType NoteProperty -Name Left -Value 0 Add-Member -InputObject $interval -MemberType NoteProperty -Name Right -Value 0 if ($id -match "(.*)-(.*)") { $interval.Left = [Convert]::ToInt32($Matches[1]) $interval.Right = [Convert]::ToInt32($Matches[2]) } else { $interval.Left = $interval.Right = [Convert]::ToInt32($id) } $intervals += $interval } $removed_files = @() foreach ($file in $all_files) { foreach ($interval in $intervals) { if ([Convert]::ToInt32($file.ID) -ge $interval.Left -and [Convert]::ToInt32($file.ID) -le $interval.Right) { $removed_files += $file break } } } $removed_files | Format-Table -AutoSize $answer = "" while ($answer.ToLower() -ne "y" -and $answer.ToLower() -ne "n") { $answer = Read-Host -Prompt $("Удалить указанные файлы в количестве {0} шт (y/n)?" -f $removed_files.Length) } if ($answer.ToLower() -eq "y") { foreach ($removed_file in $removed_files) { Remove-Item -LiteralPath $removed_file.Fullname -Force } } }
Файлы данных сценариев можно скачать, соответственно, отсюда и отсюда