Для выявления дубликатов файлов существует множество решений — от 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
Годно! Но, как обычно, в блоге публикуются решения средствами ОС / Microsoft. Можно сравнить эффективность :)
Здравствуйте !
А можно просто проверить этим скриптом наличие дубликатов файлов в операционной системе Windows 7 и сколько они занимают места лишнего в ней ? Я только проверял наличие дубликатов картинок приложением CCleaner Professional,но не удалял их так как их было очень мало порядка около 50 Мб.А вот по вопросу дубликатов файлов,тут я профан.Что вы посоветуете мне ? Всего хорошего.
конечно это требует установки дополнительного пакета, но гешефт от этого может быть несколько больше чем от только дублей файлов.
Евгений, у меня сразу несколько вопросов (думаю, риторических:)
1. Это поддерживается в клиентской Windows 10? Очевидно, контекст статьи — клиент.
2. Пакет предлагается официально к загрузке?
3. Ты сам это используешь в клиентской ОС?
1-2. По крайней мере где-то полгода назад вполне даже работало. К загрузке пакет предлагается официально, но не для клиентских ОС. И то, что целевая ОС по статье — клиент, а не сервер не очевидно, более того — учитывая суть проблемы, это должно волновать в первую очередь самого пользователя, если конечно он не устраивает такой хламник на общедоступной файлопомойке.
3. Сам нет (под линуксями живу), а вот жене настроил. Правда последний раз проверял как оно там работает все те же «около полугода назад».
Ясно, советовать неподдерживаемое решение я не буду, конечно. А то потом данные пропадут, а проклятья мне отгрузят :)
Здрасьте, у меня весь блог про клиент :)
«Слона-то я и не приметил»
Можно, конечно. Вместо создания жесткой ссылки берите размер файла и прибавляйте.
Но проверять системные файлы я бы не советовал, ибо бессмысленно. Впрочем, как и снятая с поддержки Windows 7 в 2021 году.
Вот прямо в лоб экономить место с помощью жёстких ссылок я бы не рекомендовал.
Допустим, у нас есть два идентичных файла d:\fldr1\file1.txt и d:\fldr2\file2.txt .
Сэкономили место с помощью жёсткой ссылки.
Потом появилась необходимость отредактировать файл d:\fldr1\file1.txt. И «внезапно» d:\fldr2\file2.txt, который мы вовсе не хотели править, оказался безнадёжно испорчен!
Поясните, как другой файл может быть испорчен, если это тот же самый файл?
А так, не забывайте про первоначальную цель — избавиться от дубликатов.
А как бы вы рекомендовали?
Порча логическая, а не техническая. Непредсказуемое поведение возникает из-за забывчивости, какая у нас копия, реальная или виртуальная.
Третий раз скажу — не пользуйтесь скриптом, если не хотите избавляться от дубликатов. Жесткая ссылка — это не резервная копия. В примере у меня сравниваются две резервных копии (оригинал где-то еще).
Извините, но вы кажется не поняли аргумента. Проблема во времени. Сейчас захотел избавится от дубликатов, а через полгода забыл и поступил обыденно, будто это реальная копия. Ведь внешне эти две папки ничуть не изменились и глядя на них определить виртуальность невозможно.
Это не аргумент против. Это warning, которым такой пост должен быть сопровожден, хотя бы в комментариях.
А еще такой аргумент возник.
А вы можете утверждать, что эти две древние копии сами по себе не содержат файлов с хард-линками на другие файлы — внутри копии, на оригинал, на еще какое-то место? И что с ними произойдет, если файлы между копиями совпадут?
В одном случае, мы можем разорвать одну связь и создать новую, а в другом, к существующей связи присоединить еще один элемент. И то, и другое — чревато.
Поэтому с одной стороны вы правы, захотели дедупликации — делайте. С другой же, если захотели дедупликации, хорошо подумайте… еще раз подумайте… и откажитесь :-)
Могу ошибаться, но Get-Filehash не гарантирует уникальность хэша (теоретически возможно для двух разных файлов получить одинаковый хэш — явление редкое, но тем не менее).
Наверное стоит добавить побитное сравнение для файлов с одинаковым хэшем. Для уверенности.
Хэш + размер. Быстро и эффективно.
Вообще-то с размера надо начинать. СуперБыстрая проверка гарантирующая различие. А хэш считать только при совпадающем размере. Тогда скрипт высвободит 8.5GB за 12.5 секунд, а не минут.
Первый ванлайнер был именно таким :)
Да, это правильнее. Напишите свой вариант, я добавлю в статью ;)
По идее, достаточно вставить после ChildItem
где chain, заменить на команду по смыслу обратную Group, преобразующая двухуровневый Group/Items в одноуровневый список файлов. Извините, этот язык знаю очень плохо.
Проверьте на ваших данных. Любопытно, сколько времени съэкономили 85 символов кода))
Ну вот, а говорите язык плохо знаете :) Select творит чудеса. Я внес ваш вариант в статью, спасибо!
На моем тестовом наборе времени особо не сэкономишь, потому что 95% файлов имеют дубликаты — хэш все равно вычислять надо. На других наборах, где мало дубликатов, экономия будет заметной.
Там по дефолту SHA-256, вероятностью коллизии можно пренебречь. Можете сделать SHA-512 (параметр
-Algorithm
). А побитно я бы не стал ради производительности, проще пропустить.Типичный случай: скидываю с фотокамеры снимки. В одной папке храню резервную копию (исходники с камеры), во второй обрабатываю, если нужно. Прогнали скрипт — получили вместо файла и его резервной копии один файл с разными именами. Отфотошопил в рабочей папке — изменил заодно и исходник.
Пользоваться жёсткими ссылками с умом.
А для сервера дедупликация рулит, как уже указали. Вот если бы аналог для 10-ки был.
Я так и не увидел никакой проблемы. Я же написал (и вы процитировали).
Если такой цели нет, не пользуйтесь скриптом. Ваш сценарий с фотошопом — это резервная копия. Одна. У меня в примере их две, а оригинала там нет.
Хотелось бы советов с умом.
Нашел еще один момент, где можно изменить скрипт.
Дедупликация проходит не только между dir1 и dir2, но и внутри каждой из папок. dir1/file.txt прекрасно слинкуется с dir1/backup/file.txt. Получается скрипт, изначально, по логике, может работать с одной папкой. Но при желании, с неограниченным количеством.
Я считаю, что в текущей реализации куда проще указать папки повыше уровнем и оставить его шерстить. Но вам и карты в руки!
Папки? Если выше, то «папку«. Но с одной папкой скрипт работать не умеет. Нет, оно конечно можно создать рядом с корневой папкой еще одну временную фейковую на время работы скрипта, но это костыль 88lvl.
И да, внутренний линк между dir1/file.txt и dir1/backup/file.txt может принести большие проблемы, после восстановления из резервной копии «dir1».
Поэтому речь не столько о проще, сколько о правильно. Если указываем два пути, дедуплицируем ТОЛЬКО между ними, но не внутри. Думаю именно такую логику представляют себе все читатели поста. Если дедуплицируем внутри папки, позволяем указать одну.
Кмк это какие-то фантазии. Даже оригинал является жесткой ссылкой на себя любимого, это вам даже fsutil напишет. Эксперимент с тремя файлами был в самом первом рассказе при жесткие ссылки почти 10 лет назад ¯\_(ツ)_/¯
Если не увидели логику, не спешите объявлять это фантазией. Папки буду именовать только цифрами и допустим они полностью совпадают. Этот пример легко распространяется на любой файл имеющий хардлинк ДО дедупликации.
Дедуплицировали папки 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 или неполную дедупликацию.
И надо проверять алгоритм работы командлетов, в какой из папок будет удаляться файл. Результат вполне может оказаться вообще непредсказуемым.
Не, ну я же не знал, что вы будете гонять скрипт в таких сценариях :)
Он удаляет из 2, поэтому уже имеем 1, 2+3.
Я думаю, что вы слишком серьезно подходите к вопросу. Я не декларировал
победы коммунизмадедупликации. Это просто ванлайнер из чата для быстрого и грязного решения типичной задачи :) В блоге он лишь потому, что вопрос популярный, а кода многовато для канала ТГ.Стандартный ответ программистов тестировщикам. :-)
Просто добавьте в статью два предупреждения. Тем более, что вопрос популярный.
1. Скрипт может портить существующую систему хардлинков.
2. Несмотря на интерфейс предполагающий дедупликацию между папками, она также проходит внутри самих папок, линкуя ./file.txt с ./backup/file.txt.
Оба момента могут привести к ошибкам, после восстановления данных из такого «бекапа».
ЗЫ. А еще надо подумать, как скрипт к SymLink отнесется. Теоретически тоже может удалить и захардить, нарушив логику пользователя. Ведь он почему-то создал именно символьную, а не жесткую ссылку.
Я в четвертый и последний раз скажу, что это не бэкап (он у вас в другом месте должен быть). Это то, от чего вы собрались избавиться, но поленились разобраться и удалить. Меня полностью устраивает текущее содержание статьи.
В контексте первоначального невнятного баг-репорта — да.
В принципе, эта проблема тоже решается несложно. И раз уж у нас не ванлайнер, а скрипт, должен быть исправлен. Идея такова.
Так как физического копирования не происходит, на скорость не повлияет.
Если у пользователя есть две настолько схожие папки, что к ним хочется применить дедупликацию, можете 100 раз повторить «это не бекап», но это таки будет версионным бекапом.
Выше упоминали про неотличимость ссылок от файлов… Проблема частично решается установкой приблуды HardLinkShellExt — значки на ссылках ВИЗУАЛЬНО слегка меняются, также появляется закладка в свойствах ссылки с переходом к оригиналу и функция создания любых ссылок по ПКМ.
Пользую уже давно — работает корректно, пользую в основном Junction, перенося папки разных кэшей и не только на разные SSD, распаралеливая ввод-вывод на разные физические диски.
Для борьбы с заполнением диска особенно помогает перенос папок «этих ваших огромных игрух» с всегда тесного системного диска в случае если их туда изначально заинсталлили.
Вадим, нет ли таких возможностей в 10-ке стандартно, вроде бы обсуждали это где-то?
Утилита довольно часто встречается в комментариях к моим статьям про жесткие ссылки. Сам я не очень люблю обвешивать проводник расширениями.
Только для магазинных игр, ищите Save Locations в пуске. См. также Фишки Windows 10: как грамотно переместить пользовательские папки на другой диск.
Надо еще проверять не являются ли файлы и так жесткими ссылками. Может их и удалять не имеет смысла.
Это хорошая идея, но почему-то мне кажется, что проверка существенно замедлит скрипт.
По многочисленным просьбам в статью добавлено предупреждение. Дальнейшие претензии к скрипту и тексту статьи я прошу мне не доставлять.
Я использую «AllDup». Очень гибко, удобно, многофункционально и на русском. ))
Ознакомиться подробней можно здесь — https://ammo1.livejournal.com/586886.html
Годно, спасибо!