Skip to content
it255ruPublic

About

Duplicate file finder CLI with safe deletion: byte-for-byte verification and dry-run. Поиск дубликатов файлов с безопасным удалением.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

duplo

duplo - созвучно с «дупло» (укромное место, где что-то спрятано) и английским duplicate.

CLI-утилита для поиска дубликатов файлов и каталогов с возможностью удаления.

Документ соответствует ревизии кода на 2026-09-25.

Статус и известные ограничения

Проект в разработке. Режимы удаления (--interactive, --auto-first) в текущей ревизии небезопасны. Перед любым удалением сделайте резервную копию данных.

Известные дефекты, которые могут привести к потере данных:

  • --find-identical-dirs сравнивает каталоги только по файлам, у которых есть дубликат. Уникальные файлы и подкаталоги при сравнении не учитываются. Каталог, признанный «идентичным», удаляется целиком (shutil.rmtree) вместе с уникальным содержимым.
  • Выбор по файлам и выбор по каталогам не согласуются между собой. Комбинация «оставить первый файл» и «оставить последний каталог» может удалить все копии.
  • В ручном режиме ([m]) пустой ввод, ввод через запятую или номер вне диапазона приводят к удалению всех копий группы.
  • Symlink на файл считается дубликатом своей цели. Удалённым может оказаться оригинал, а сохранённой — ссылка.
  • Все пустые файлы (__init__.py, .gitkeep, .nomedia) считаются дубликатами друг друга.
  • Перед удалением файл не сверяется повторно: решение принимается по MD5 и по кэшу (путь, размер, mtime).
  • --dry-run не показывает содержимое подкаталогов, которые будут удалены. Реальный объём удаления может быть больше показанного.

Прочие ограничения:

  • --workers принимается, но не используется: хеширование выполняется в одном потоке.
  • Кэш хешей хранится в формате pickle в текущем каталоге. Загрузка чужого или подменённого файла кэша приводит к выполнению произвольного кода. Не запускайте утилиту из каталогов, доступных на запись другим пользователям, и не используйте кэш-файлы из ненадёжных источников.
  • Hardlink'и считаются дубликатами. Их удаление не освобождает место, но учитывается в отчёте как освобождённое.
  • Код возврата всегда 0, в том числе при несуществующем каталоге и при ошибках удаления. Для использования в скриптах результат нужно проверять по выводу.
  • На Linux и macOS FIFO в сканируемом дереве приводит к зависанию.
  • На Windows файлы с атрибутом read-only не удаляются. Удаление каталога с таким файлом прерывается на середине, и каталог остаётся удалённым частично.

Безопасный режим использования на текущей ревизии — только анализ, без флагов удаления.

Возможности

  • Поиск дубликатов файлов: группировка по размеру, затем сравнение MD5.
  • Статистика по категориям, расширениям и каталогам.
  • Поиск каталогов с совпадающим набором дублирующихся файлов (см. ограничения выше).
  • Классификация файлов по расширению: images, videos, audio, documents, archives, executables, scripts, data, system, fonts, design, other.
  • Кэш хешей для повторных запусков.
  • Интерактивный и автоматический выбор копий для удаления, режим --dry-run.

Требования

  • Python 3.7 или новее. Python 3.7 и 3.8 больше не получают обновлений безопасности, рекомендуется 3.9+.
  • Внешних зависимостей нет, используется только стандартная библиотека.
  • Права на чтение сканируемого каталога. Для удаления нужны права на запись.

Установка

git clone https://github.com/it255ru/duplo.git
cd duplo

Виртуальное окружение не обязательно, так как зависимостей нет.

Использование

Анализ каталога без удаления:

python main.py /path/to/dir

Группировка дубликатов по категориям:

python main.py /path/to/dir --group-by-category

Поиск совпадающих каталогов (только отчёт):

python main.py /path/to/dir --find-identical-dirs

Просмотр плана удаления без выполнения:

python main.py /path/to/dir --interactive --dry-run

Интерактивное удаление (см. раздел «Статус и известные ограничения»):

python main.py /path/to/dir --interactive

Автоматическое удаление всех копий, кроме первой. «Первая» определяется порядком обхода файловой системы, а не алфавитом или датой. На FAT32 и exFAT это порядок создания записей в каталоге.

python main.py /path/to/dir --auto-first

Перед фактическим удалением утилита выводит план и запрашивает подтверждение (y/n).

Аргументы

Аргумент Описание По умолчанию
source_dir Каталог для анализа обязательный
--workers N Зарезервирован, в текущей ревизии не используется 8
--cache-file PATH Файл кэша хешей (pickle) hash_cache.pkl в текущем каталоге
--group-by-category Выводить дубликаты по категориям выключено
--find-identical-dirs Искать совпадающие каталоги выключено
--interactive Интерактивный выбор копий для удаления выключено
--auto-first Оставить первую копию в каждой группе выключено
--dry-run Показать план удаления без выполнения выключено

Алгоритм

  1. Рекурсивный обход source_dir через os.walk. Для каждого файла фиксируются путь, размер и mtime. Каталоги и файлы, недоступные для чтения, пропускаются без сообщения.
  2. Группировка по размеру. Файлы с уникальным размером дальше не обрабатываются.
  3. Для кандидатов вычисляется MD5. Если в кэше есть запись с тем же путём, размером и mtime, хеш берётся из кэша.
  4. Группы с одинаковым MD5 считаются дубликатами. Побайтовое сравнение не выполняется.
  5. С --find-identical-dirs для каждого каталога строится отсортированный список хешей его файлов-дубликатов. Каталоги с одинаковым списком объединяются в группу.

Пример вывода

============================================================
СВОДНАЯ СТАТИСТИКА
============================================================
Общее количество файлов: 21603
Общий объем данных: 113.97 GB

Группа 1 (Хеш: 637bfe87...), Размер: 2.20 MB, Категория: images
  -> X:\photos\CIMG2033.JPG
  -> X:\photos\Копия CIMG2033.JPG

Группа идентичных каталогов #1:
  [1] X:\photos\a\2014-01 (5 файлов, 6.02 MB)
  [2] X:\photos\b\2014-01 (5 файлов, 6.02 MB)

Количество файлов и размер в группе каталогов относятся только к файлам, лежащим непосредственно в каталоге, без подкаталогов. Совпадение этих чисел не означает совпадения содержимого.

Структура проекта

duplo/
├── main.py
├── README.md
└── LICENSE

Файл кэша (hash_cache.pkl или путь из --cache-file) создаётся при запуске и не должен попадать в репозиторий. Добавьте его в .gitignore.

Лицензия

MIT, см. LICENSE.

Участие в разработке

Issues и pull requests принимаются через GitHub. К исправлениям, которые касаются удаления данных, прикладывайте тест, воспроизводящий проблему на текущей ревизии.

About

Duplicate file finder CLI with safe deletion: byte-for-byte verification and dry-run. Поиск дубликатов файлов с безопасным удалением.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages