Данный сценарий находит в указанных папках файлы с одинаковым размером и на основании MD5-хэша отбирает из них файлы с одинаковым содержимым
function Find-EqualFile { <# .SYNOPSIS Командлет предназначен для поиска одинаковых файлов .DESCRIPTION Командлет находит в указанных папках файлы с одинаковым размером и на основании MD5-хэша отбирает из них файлы с одинаковым содержимым .PARAMETER Folders Папки, в которых необходимо найти одинаковые файлы .PARAMETER OutputCSV Путь к CSV-файлу со списком одинаковых файлов .INPUTS - .OUTPUTS - .NOTES Версия 1.3 (c) 2019 Александр Галков, alexander@galkov.pro .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 Александр Галков, alexander@galkov.pro .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 } } } |
Файлы данных сценариев можно скачать, соответственно, отсюда и отсюда