
Недавно мне нужно было удалить много дубликатов. Я объединяю три или четыре файловые системы и хочу, чтобы пространство использовалось экономно. Сначала fdupes
казалось, что это лучший инструмент для работы, но я все чаще сталкиваюсь с ограничениями.
Рассмотрим команду fdupes -rdN somedirectory/
. Она создает хеш всех файлов в подкаталогах somedirectory.
А когда он обнаруживает дубликаты, он их удаляет, так что остается только одна копия всего.
Но что делать, если я хочу сохранить somedirectory/subdirectory1/somefile
, а на самом деле дубликатов четыре, и программа сначала встречает один из дубликатов? Затем она удаляет somedirectory/subdirectory1/somefile
, чего я не хочу.
Я хочу иметь возможность указать, каким-то образом, какие дубликаты сохранять. И пока ни одна из стандартных программ для работы с дубликатами (duff, FSLint) не позволяет автоматизировать такое поведение. Я бы предпочел не заниматься собственными разработками, поэтому и задаю этот вопрос.
Я хотел бы иметь возможность написать что-то вроде
killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
решение1
Пока функционал, который вы ищете, отсутствует на складе fdupes
, я разветвилfdupes
(моя вилка называется jdupes
)и добавили некоторые функции, которые могут решить эту проблему при определенных обстоятельствах. Например, в указанном случае, когда вы хотите сохранить somedirectory/subdirectory1/somefile
при автоматическом удалении дубликаты ( переключатели d
и N
вместе) и нет отдельных файлов непосредственно под somedirectory
, jdupes
можно скормить каждому непосредственному пути подкаталога subdirectory1
first и -O
переключатель (который сортирует файлы по порядку параметров командной строки first):
jdupes -rdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3
Это автоматически удалит все файлы, кроме одного, в дублирующем наборе и гарантирует, что если набор содержит файл, то somedirectory/subdirectory1
он будет первым, тем самым автоматически становясь сохраненным файлом в наборе. У этого подхода все еще есть явные ограничения, такие как тот факт, что другой дубликат somedirectory/subdirectory1
может быть сохранен вместо того, который вы хотели сохранить, но в большом количестве случаев, таких как ваш, jdupes
опция порядка параметров в качестве обходного пути достаточно хороша.
В ближайшем будущем я планирую добавить систему фильтрации, jdupes
которая позволит осуществлять огромный контроль над включением/исключением файлов, сохранением для -N
действий и применением таких «стеков фильтров» на глобальном уровне или на основе параметров. Эта функция крайне необходима; я представляю себе что-то вроде этого для «автоматического удаления ненулевых дубликатов рекурсивно, НО всегда сохранять somedirectory/subdirectory1/somefile
как есть»:
jdupes -rdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/
ОБНОВЛЕНИЕ (01.03.2022):Взгляните на -X
расширенные параметры фильтров, добавленные в 2020 году. Это не совсем то, что вам нужно, но фильтры nostr
и onlystr
позволяют указывать подстроки в полном пути, которые следует игнорировать или требовать.
решение2
Я не видел этого нигде: Скажите, что вы хотите это. У вас есть /mnt/folder-tree-1 /mnt/folder-tree-2. Вы не хотите удалять все дубликаты, но если файл существует в tree-2, и идентичный файл существует в tree-1 с точно таким же путем и именем, удалите его из tree-2.
Предупреждение: это довольно кратко, и если вы попытаетесь скопировать и вставить это, имея ограниченные навыки работы с оболочкой, будьте осторожны.
fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt
fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line
do
if grep -q "`echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|'`" dupes-all.txt
then
echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2//|')\"
fi
done > rm-v2-dupes.sh
Или все в одной строке:
fdupes -rn /mnt/folder-tree-1/ /mnt/folder-tree-2/ > dupes-all.txt; fgrep /mnt/folder-tree-1/ dupes-all.txt | while read line; do if grep -q "`echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|'`" dupes-all.txt; then echo rm \"$(echo $line | sed -e 's|^/mnt/folder-tree-1/|/mnt/folder-tree-2/|')\"; fi; done > rm-v2-dupes.sh
После этого проверьте и выполните rm-v2-dupes.sh
решение3
А как насчет жесткого связывания дублирующихся файлов? Таким образом, пространство используется только один раз, но они все еще существуют во всех путях. Загвоздка в том, что жестко связанные файлы должны быть изменены на месте (они должны быть изменены только путем удаления файла и повторного создания его с новым содержимым). Другой подход заключается в том, чтобы создать символическую ссылку на файлы вместе, хотя у вас есть та же проблема с выбором «первичного» файла. Это можно сделать с помощью следующего скрипта (хотя обратите внимание, что он не обрабатывает имена файлов, содержащие пробелы).
fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
for DEST in $DESTS; do
ln -f $SOURCE $DEST
done
done
решение4
Просто чтобы добавить изюминку к предыдущему ответу. Я использовал следующий код несколько раз, немного изменив предыдущий ответ с помощью простого | grep
способа изолировать папку, из которой я хочу удалить.
`fdupes -r -n -S /directory | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`
Опять же, это создаст sh-файл для удаления всех перечисленных файлов, без закомментированных строк. Конечно, вы все равно можете редактировать файл, чтобы закомментировать определенные строки/файлы, которые вы хотите сохранить.
Еще один совет для больших каталогов — запустить fdupes в txt-файле, а затем экспериментировать с ним, | grep
пока | sed
не получу желаемый результат.
`fdupes -r -n -S /directory > duplicate-files.txt`
`cat duplicate-files.txt | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`