Для выявления дубликатов файлов существует множество решений — от Total Commander до специализированных утилит. Но в моей практике почти все сравнения заканчивались так: не удаляй ничего, я потом разберу! "Потом" — это примерно в следующем веке, а место высвободить нужно уже сейчас.
Задачу "не удалять дубликаты, но сэкономить место" можно решить с помощью жестких ссылок.
Василий Гусев как-то раз написал на коленке несложный скрипт PowerShell, и с тех пор он не раз всплывал в разных чатах в качестве решения. В оригинале был ванлайнер.
ls -Recurse dir1, dir2 | Get-FileHash | group hash | where count -ge 2 | %{$f1 = $_.group[0].path; rm $f1; fsutil hardlink create $f1 $_.group[1].path}
Я слегка причесал скрипт, а Василий сделал проверку параметров (путей к папкам), чтобы не случилось нежелательного. И скрипт отправился в блог.
Однако в комментариях к записи читатель Lecron справедливо заметил, что для ускорения работы имеет смысл сначала сверять размер файлов, а хэш вычислять только при совпадении размера. Он подкрепил слова делом, и ниже вариант с учетом его доработок.
Скрипт предоставляется с демонстрационными целями в качестве информации к размышлению и отправной точки для создания вашего идеального скрипта. Он не подходит для любой ситуации и не годится для тех, кто плохо понимает принцип работы жестких ссылок, страдает рассеянностью, забывчивостью и т.д. Я не несу никакой ответственности за любые потери данных, негативные эмоции и боль после использования скрипта. Делайте резервные копии и проверяйте их целостность.
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:\fldr1 -dir2 d:\fldr2
Я дважды и относительно недавно показывал выборку дубликатов в блоге (1, 2), поэтому общий подход вам знаком.
- Get-ChildItem получает список всех файлов в сравниваемых папках, затем файлы группируются по размеру.
- Если совпадений размера не менее двух, мы имеем дело с потенциальными дубликатами файла. Выбираем их с Select-Object и вычисляем хэш с Get-FileHash.
- Файлы группируются по хэшу.
- Если совпадений хэша более двух, дубликаты удаляются, а вместо них создаются жесткие ссылки. Начиная с PowerShell 5.0, вместо утилиты fsutil можно использовать командлет New-Item для создания ссылок NTFS.
Для теста у меня на внешнем диске нашлись бэкапы изображений и документов из профиля в двух похожих папках:
- April 2016 (9445 Files, 935 Folders)
- November 2016 (10005 Files, 937 Folders)
Понятно, что там примерно одно и то же, но разбираться лень. Скрипт высвободил 8.5GB за 12.5 минут. Времени понадобилось относительно много, потому что примерно у 95% файлов были дубликаты, и для них вычислялся хэш.
А как вы боретесь с дубликатами файлов? Напишите в комментариях!
Олег Жаров
Уже лет пять для тех же целей использую DupeMerge https://schinagl.priv.at/nt/dupemerge/dupemerge.html
Vadim Sterkin
Годно! Но, как обычно, в блоге публикуются решения средствами ОС / Microsoft. Можно сравнить эффективность :)
Владимир Шведов
Здравствуйте !
А можно просто проверить этим скриптом наличие дубликатов файлов в операционной системе Windows 7 и сколько они занимают места лишнего в ней ? Я только проверял наличие дубликатов картинок приложением CCleaner Professional,но не удалял их так как их было очень мало порядка около 50 Мб.А вот по вопросу дубликатов файлов,тут я профан.Что вы посоветуете мне ? Всего хорошего.
Евгений Генеральчик
конечно это требует установки дополнительного пакета, но гешефт от этого может быть несколько больше чем от только дублей файлов.
Vadim Sterkin
Евгений, у меня сразу несколько вопросов (думаю, риторических:)
1. Это поддерживается в клиентской Windows 10? Очевидно, контекст статьи — клиент.
2. Пакет предлагается официально к загрузке?
3. Ты сам это используешь в клиентской ОС?
Евгений Генеральчик
1-2. По крайней мере где-то полгода назад вполне даже работало. К загрузке пакет предлагается официально, но не для клиентских ОС. И то, что целевая ОС по статье — клиент, а не сервер не очевидно, более того — учитывая суть проблемы, это должно волновать в первую очередь самого пользователя, если конечно он не устраивает такой хламник на общедоступной файлопомойке.
3. Сам нет (под линуксями живу), а вот жене настроил. Правда последний раз проверял как оно там работает все те же «около полугода назад».
Vadim Sterkin
Ясно, советовать неподдерживаемое решение я не буду, конечно. А то потом данные пропадут, а проклятья мне отгрузят :)
Здрасьте, у меня весь блог про клиент :)
Евгений Генеральчик
«Слона-то я и не приметил»
Vadim Sterkin
Можно, конечно. Вместо создания жесткой ссылки берите размер файла и прибавляйте.
Но проверять системные файлы я бы не советовал, ибо бессмысленно. Впрочем, как и снятая с поддержки Windows 7 в 2021 году.
Veska
Вот прямо в лоб экономить место с помощью жёстких ссылок я бы не рекомендовал.
Допустим, у нас есть два идентичных файла d:\fldr1\file1.txt и d:\fldr2\file2.txt .
Сэкономили место с помощью жёсткой ссылки.
Потом появилась необходимость отредактировать файл d:\fldr1\file1.txt. И «внезапно» d:\fldr2\file2.txt, который мы вовсе не хотели править, оказался безнадёжно испорчен!
Vadim Sterkin
Поясните, как другой файл может быть испорчен, если это тот же самый файл?
А так, не забывайте про первоначальную цель — избавиться от дубликатов.
А как бы вы рекомендовали?
Lecron
Порча логическая, а не техническая. Непредсказуемое поведение возникает из-за забывчивости, какая у нас копия, реальная или виртуальная.
Vadim Sterkin
Третий раз скажу — не пользуйтесь скриптом, если не хотите избавляться от дубликатов. Жесткая ссылка — это не резервная копия. В примере у меня сравниваются две резервных копии (оригинал где-то еще).
Lecron
Извините, но вы кажется не поняли аргумента. Проблема во времени. Сейчас захотел избавится от дубликатов, а через полгода забыл и поступил обыденно, будто это реальная копия. Ведь внешне эти две папки ничуть не изменились и глядя на них определить виртуальность невозможно.
Это не аргумент против. Это warning, которым такой пост должен быть сопровожден, хотя бы в комментариях.
Lecron
А еще такой аргумент возник.
А вы можете утверждать, что эти две древние копии сами по себе не содержат файлов с хард-линками на другие файлы — внутри копии, на оригинал, на еще какое-то место? И что с ними произойдет, если файлы между копиями совпадут?
В одном случае, мы можем разорвать одну связь и создать новую, а в другом, к существующей связи присоединить еще один элемент. И то, и другое — чревато.
Поэтому с одной стороны вы правы, захотели дедупликации — делайте. С другой же, если захотели дедупликации, хорошо подумайте… еще раз подумайте… и откажитесь :-)
S Kho
Могу ошибаться, но Get-Filehash не гарантирует уникальность хэша (теоретически возможно для двух разных файлов получить одинаковый хэш — явление редкое, но тем не менее).
Наверное стоит добавить побитное сравнение для файлов с одинаковым хэшем. Для уверенности.
Lecron
Хэш + размер. Быстро и эффективно.
Вообще-то с размера надо начинать. СуперБыстрая проверка гарантирующая различие. А хэш считать только при совпадающем размере. Тогда скрипт высвободит 8.5GB за 12.5 секунд, а не минут.
Vadim Sterkin
Первый ванлайнер был именно таким :)
Да, это правильнее. Напишите свой вариант, я добавлю в статью ;)
Lecron
По идее, достаточно вставить после ChildItem
где chain, заменить на команду по смыслу обратную Group, преобразующая двухуровневый Group/Items в одноуровневый список файлов. Извините, этот язык знаю очень плохо.
Lecron
Проверьте на ваших данных. Любопытно, сколько времени съэкономили 85 символов кода))
Vadim Sterkin
Ну вот, а говорите язык плохо знаете :) Select творит чудеса. Я внес ваш вариант в статью, спасибо!
На моем тестовом наборе времени особо не сэкономишь, потому что 95% файлов имеют дубликаты — хэш все равно вычислять надо. На других наборах, где мало дубликатов, экономия будет заметной.
Vadim Sterkin
Там по дефолту SHA-256, вероятностью коллизии можно пренебречь. Можете сделать SHA-512 (параметр
-Algorithm
). А побитно я бы не стал ради производительности, проще пропустить.Veska
Типичный случай: скидываю с фотокамеры снимки. В одной папке храню резервную копию (исходники с камеры), во второй обрабатываю, если нужно. Прогнали скрипт — получили вместо файла и его резервной копии один файл с разными именами. Отфотошопил в рабочей папке — изменил заодно и исходник.
Пользоваться жёсткими ссылками с умом.
А для сервера дедупликация рулит, как уже указали. Вот если бы аналог для 10-ки был.
Vadim Sterkin
Я так и не увидел никакой проблемы. Я же написал (и вы процитировали).
Если такой цели нет, не пользуйтесь скриптом. Ваш сценарий с фотошопом — это резервная копия. Одна. У меня в примере их две, а оригинала там нет.
Хотелось бы советов с умом.
Lecron
Нашел еще один момент, где можно изменить скрипт.
Дедупликация проходит не только между dir1 и dir2, но и внутри каждой из папок. dir1/file.txt прекрасно слинкуется с dir1/backup/file.txt. Получается скрипт, изначально, по логике, может работать с одной папкой. Но при желании, с неограниченным количеством.
Vadim Sterkin
Я считаю, что в текущей реализации куда проще указать папки повыше уровнем и оставить его шерстить. Но вам и карты в руки!
Lecron
Папки? Если выше, то «папку«. Но с одной папкой скрипт работать не умеет. Нет, оно конечно можно создать рядом с корневой папкой еще одну временную фейковую на время работы скрипта, но это костыль 88lvl.
И да, внутренний линк между dir1/file.txt и dir1/backup/file.txt может принести большие проблемы, после восстановления из резервной копии «dir1».
Поэтому речь не столько о проще, сколько о правильно. Если указываем два пути, дедуплицируем ТОЛЬКО между ними, но не внутри. Думаю именно такую логику представляют себе все читатели поста. Если дедуплицируем внутри папки, позволяем указать одну.
Vadim Sterkin
Кмк это какие-то фантазии. Даже оригинал является жесткой ссылкой на себя любимого, это вам даже fsutil напишет. Эксперимент с тремя файлами был в самом первом рассказе при жесткие ссылки почти 10 лет назад ¯\_(ツ)_/¯
Lecron
Если не увидели логику, не спешите объявлять это фантазией. Папки буду именовать только цифрами и допустим они полностью совпадают. Этот пример легко распространяется на любой файл имеющий хардлинк ДО дедупликации.
Дедуплицировали папки 1 и 2, получив 1+2.
Появляется папка 3.
Запускаем hardlinks.ps 2 3. Скрипт удаляет файл из 3 и создает ссылку на 2. Все хорошо, имеем 3 ссылки 1+2+3.
Запускаем hardlinks.ps 3 2. (поменяли порядок параметров). Скрипт удаляет файл из 2, чем разрывает ссылку 1+2, и создает ссылку на 3. Имеем 1,2+3 или неполную дедупликацию.
И надо проверять алгоритм работы командлетов, в какой из папок будет удаляться файл. Результат вполне может оказаться вообще непредсказуемым.
Vadim Sterkin
Не, ну я же не знал, что вы будете гонять скрипт в таких сценариях :)
Он удаляет из 2, поэтому уже имеем 1, 2+3.
Я думаю, что вы слишком серьезно подходите к вопросу. Я не декларировал
победы коммунизмадедупликации. Это просто ванлайнер из чата для быстрого и грязного решения типичной задачи :) В блоге он лишь потому, что вопрос популярный, а кода многовато для канала ТГ.Lecron
Стандартный ответ программистов тестировщикам. :-)
Просто добавьте в статью два предупреждения. Тем более, что вопрос популярный.
1. Скрипт может портить существующую систему хардлинков.
2. Несмотря на интерфейс предполагающий дедупликацию между папками, она также проходит внутри самих папок, линкуя ./file.txt с ./backup/file.txt.
Оба момента могут привести к ошибкам, после восстановления данных из такого «бекапа».
ЗЫ. А еще надо подумать, как скрипт к SymLink отнесется. Теоретически тоже может удалить и захардить, нарушив логику пользователя. Ведь он почему-то создал именно символьную, а не жесткую ссылку.
Vadim Sterkin
Я в четвертый и последний раз скажу, что это не бэкап (он у вас в другом месте должен быть). Это то, от чего вы собрались избавиться, но поленились разобраться и удалить. Меня полностью устраивает текущее содержание статьи.
В контексте первоначального невнятного баг-репорта — да.
Lecron
В принципе, эта проблема тоже решается несложно. И раз уж у нас не ванлайнер, а скрипт, должен быть исправлен. Идея такова.
Так как физического копирования не происходит, на скорость не повлияет.
Lecron
Если у пользователя есть две настолько схожие папки, что к ним хочется применить дедупликацию, можете 100 раз повторить «это не бекап», но это таки будет версионным бекапом.
Alien Mars
Выше упоминали про неотличимость ссылок от файлов… Проблема частично решается установкой приблуды HardLinkShellExt — значки на ссылках ВИЗУАЛЬНО слегка меняются, также появляется закладка в свойствах ссылки с переходом к оригиналу и функция создания любых ссылок по ПКМ.
Пользую уже давно — работает корректно, пользую в основном Junction, перенося папки разных кэшей и не только на разные SSD, распаралеливая ввод-вывод на разные физические диски.
Для борьбы с заполнением диска особенно помогает перенос папок «этих ваших огромных игрух» с всегда тесного системного диска в случае если их туда изначально заинсталлили.
Вадим, нет ли таких возможностей в 10-ке стандартно, вроде бы обсуждали это где-то?
Vadim Sterkin
Утилита довольно часто встречается в комментариях к моим статьям про жесткие ссылки. Сам я не очень люблю обвешивать проводник расширениями.
Только для магазинных игр, ищите Save Locations в пуске. См. также Фишки Windows 10: как грамотно переместить пользовательские папки на другой диск.
Herz Mein
Надо еще проверять не являются ли файлы и так жесткими ссылками. Может их и удалять не имеет смысла.
Vadim Sterkin
Это хорошая идея, но почему-то мне кажется, что проверка существенно замедлит скрипт.
Vadim Sterkin
По многочисленным просьбам в статью добавлено предупреждение. Дальнейшие претензии к скрипту и тексту статьи я прошу мне не доставлять.
Владимир Педяш
Я использую «AllDup». Очень гибко, удобно, многофункционально и на русском. ))
Ознакомиться подробней можно здесь — https://ammo1.livejournal.com/586886.html
Vadim Sterkin
Годно, спасибо!