
Для одного из проектов мне понадобился сценарий PowerShell для поиска дубликатов файлов в общей сетевой папке файлового сервера. Существует ряд сторонних инструментов для поиска и удаления дубликатов файлов в Windows, но большинство из них являются коммерческими или не подходят для автоматических сценариев.
Поскольку файлы могут иметь разные имена, но идентичное содержимое, не следует сравнивать файлы только по имени. Лучше получить хэши всех файлов и найти среди них одинаковые.
Следующая однострочная команда PowerShell позволяет рекурсивно просканировать папку (включая ее подпапки) и найти дубликаты файлов. В этом примере были найдены два одинаковых файла с одинаковыми хэшами:
Get-ChildItem –path C:\Share\ -Recurse | Get-FileHash | Group-Object -property hash | Where-Object { $_.count -gt 1 } | ForEach-Object { $_.group | Select-Object Path, Hash }
Этот однострочник PowerShell прост в использовании для поиска дубликатов, однако его производительность довольно низкая. Если в папке много файлов, вычисление их хэшей займет много времени. Проще сначала сравнить файлы по размеру (это готовый атрибут файла, который не нужно вычислять). Тогда мы получим хэши только файлов с одинаковым размером:
$file_dublicates = Get-ChildItem –path C:\Share\ -Recurse| Group-Object -property Length| Where-Object { $_.count -gt 1 }| Select-Object –Expand Group| Get-FileHash | Group-Object -property hash | Where-Object { $_.count -gt 1 }| ForEach-Object { $_.group | Select-Object Path, Hash }
Вы можете сравнить производительность обеих команд на тестовой папке с помощью функции Команда Measure-Command:
Measure-Command {your_powershell_command}
Для папки, содержащей 2 000 файлов, вторая команда работает гораздо быстрее первой (10 минут против 3 секунд).
Можно предложить пользователю выбрать дубликаты файлов для удаления. Для этого передайте список дубликатов файлов в команду Out-GridView:
$file_dublicates | Out-GridView -Title "Select files to delete" -OutputMode Multiple –PassThru|Remove-Item –Verbose –WhatIf
Пользователь может выбрать файлы для удаления в таблице (чтобы выбрать несколько файлов, нажмите и удерживайте кнопку CTRL) и нажмите OK.
Также можно заменить дублирующиеся файлы жесткими ссылками. Такой подход позволяет сохранить файлы на своих местах и значительно экономит дисковое пространство.
param(
[Parameter(Mandatory=$True)]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]$dir1,
[Parameter(Mandatory=$True)]
[ValidateScript({(Test-Path -Path $_ -PathType Container) -and $_ -ne $dir1})]
[string]$dir2
)
Get-ChildItem -Recurse $dir1, $dir2 |
Group-Object Length | Where-Object {$_.Count -ge 2} |
Select-Object -Expand Group | Get-FileHash |
Group-Object hash | Where-Object {$_.Count -ge 2} |
Foreach-Object {
$f1 = $_.Group[0].Path
Remove-Item $f1
New-Item -ItemType HardLink -Path $f1 -Target $_.Group[1].Path | Out-Null
#fsutil hardlink create $f1 $_.Group[1].Path
}
Чтобы запустить файл, используйте следующий формат команды:
.\hardlinks.ps1 -dir1 d:\folder1 -dir2 d:\folder2
Сценарий можно использовать для поиска и замены дубликатов статических файлов (которые не меняются!) с символическими жесткими ссылками.
Также можно использовать консоль dupemerge инструмент для замены дублирующихся файлов жесткими ссылками.