Есть ли способ удаления дубликатов, более точный, чем fdupes -rdN?

22

В последнее время мне необходимо удалить много дубликатов. Я объединяю три или четыре файловые системы и хочу, чтобы пространство использовалось экономно. Поначалу fdupesказалось, что это лучший инструмент для работы, но я все больше сталкиваюсь с ограничениями.

Рассмотрим команду fdupes -rdN somedirectory/. Это делает хэш всех файлов в подкаталогах некоторого каталога.

И когда он встречает дубликаты, он удаляет их, так что остается всего одна копия всего.

Но что, если я захочу сохранить, somedirectory/subdirectory1/somefileи на самом деле будет четыре дубликата, и программа сначала обнаружит один из дубликатов? Затем он удаляет somedirectory/subdirectory1/somefile, что я не хочу.

Я хочу как-то указать, какие дубликаты сохранить. И до сих пор ни одна из стандартных программ для работы с дубликатами (duff, FSLint), по-видимому, не позволяет автоматизировать такого рода поведение. Я бы предпочел не кататься, поэтому я задаю этот вопрос.

Я хотел бы иметь возможность написать что-то вроде

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
ixtmixilix
источник
Я искал то же самое, и я нашел этот superuser.com/a/561207/218922
Алексис

Ответы:

5

Хотя нужная вам функция недоступна в наличии fdupes, я раздвоил fdupes (называется моя форк jdupes) и добавил некоторые функции, которые могут решить эту проблему при определенных обстоятельствах. Например, в указанном случае, когда вы хотите сохранить somedirectory/subdirectory1/somefileпри автоматическом удалении дубликатов (и dи Nпереключатели вместе), и нет сразу же отдельных файлов somedirectory, jdupesможно указать каждый непосредственный путь к подкаталогу subdirectory1первым и -Oключом (который сортирует файлы по команде -линейный параметр порядка первым):

jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3

Это автоматически удалит все файлы, кроме одного, в дублирующем наборе и будет гарантировать, что, если в наборе есть файл, somedirectory/subdirectory1он будет первым, что автоматически станет сохраненным файлом в наборе. У этого подхода все еще есть явные ограничения, такие как тот факт, что somedirectory/subdirectory1может быть сохранен другой дубликат вместо того, который вы хотели бы сохранить, но во многих случаях, таких как ваш, jdupesопция порядка параметров в качестве обходного пути достаточно хороша.

В ближайшем будущем я планирую добавить систему фильтрации jdupes, которая обеспечит огромный контроль над включением / исключением файлов, сохранением для -Nдействий и применением таких «наборов фильтров» как на глобальном уровне, так и для каждого параметра. Эта функция крайне необходима; Я предполагаю что-то вроде этого, чтобы "автоматически удалять ненулевые дубликаты рекурсивно, НО всегда сохранять somedirectory/subdirectory1/somefileкак есть":

jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/

Джоди Ли Брухон
источник
4

Как насчет жесткого связывания дубликатов файлов вместе? Таким образом, пространство используется только один раз, но они все еще существуют на всех путях. Суть в том, что жестко связанные файлы должны быть изменены на месте (их следует изменять только удаляя файл и создавая его заново с новым содержимым). Другой подход состоит в том, чтобы объединить файлы вместе, хотя у вас есть одна и та же проблема - решить, какой файл является «основным». Это можно сделать с помощью следующего сценария (хотя обратите внимание, что он не обрабатывает имена файлов, содержащие пробелы).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
mgorven
источник
1
Использование jdupesвместо fdupesвас может просто пойти, jdupes -nrL somedirectory/что намного быстрее.
Джоди Ли Брухон
1
Опечатка в ссылке на jdupes. Удобная ссылка: github.com/jbruchon/jdupes
Ройс Уильямс
4

Я нигде не видел этого: Скажи, что ты хочешь, это. У вас есть / 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.

Гот Лунд
источник
4

У меня такой же вопрос. Если у вас много дубликатов, fdupes /my/directory/ -rdNсохраните файл с самой старой датой изменения, или если несколько файлов имеют одинаковую дату изменения, то файл будет найден первым.

Если дата изменения не важна для вас, вы можете touchсохранить файлы в каталоге, который хотите сохранить. Если вы выберете для touchних текущую дату и время, то fdupes -rdNiсохраните их с текущей датой. Или вы можете touchсохранить файлы с более ранней датой, чем те, которые вы хотите удалить, и использовать их fdupes -rdNкак обычно.

Если вам нужно сохранить дату изменения, вам нужно будет использовать один из других методов.

pheon
источник
3

Просто чтобы добавить поворот к предыдущему ответу. Я использовал следующий код несколько раз, слегка изменив предыдущий ответ, просто | grepизолировав папку, из которой я хочу удалить.

`fdupes -r -n -S /directory | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`

Опять же, это создаст sh-файл для удаления всех файлов в списке, без комментариев. Конечно, вы все равно можете отредактировать файл, чтобы закомментировать определенные строки / файлы, которые вы хотите сохранить.

Еще один совет для больших каталогов - запускать fdupes в текстовый файл, затем экспериментировать с | 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`
JFL
источник
2

Используйте sedдля создания файла оболочки, который будет содержать закомментированные команды для удаления каждого из ваших дубликатов файлов:

fdupes -r -n -S /directory | sed -r "s/^/#rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh

Полученный remove-duplicate-files.shфайл, который мы только что создали, будет закомментирован в каждой строке. Раскомментируйте файлы, которые вы хотите удалить. Тогда беги sh remove-duplicate-files.sh. Вуаля!

ОБНОВИТЬ

Ну, если вы не хотите удалять файлы только в определенных каталогах, это так просто :

fdupes -S /directory|sed '/^$/d' |sed -r "s/^[0-9]/#&/" > duple_list

python exclude_duplicates.py -f /path/to/dupe_list --delimiter='#' --keep=/full/path/to/protected/directory1,/full/path/to/protected/directory2\ with\ spaces\ in\ path >remove-duplicate-files-keep-protected.sh

Где exclude_duplicates.pyнаходится:

#/usr/bin/python
# -*- coding: utf-8 -*-
# exclude_duplicates.py
"""
THE SCRIPT DOESN'T DELETE ANYTHING, IT ONLY GENERATES TEXT OUTPUT.
Provided a list of duplicates, such as fdupes or fslint output,
generate a bash script that will have all duplicates in protected
directories commented out. If none of the protected duplicates are
found in a set of the same files, select a random unprotected
duplicate for preserving.
Each path to a file will be transformed to an `rm "path"` string which
will be printed to standard output.     
"""

from optparse import OptionParser
parser = OptionParser()
parser.add_option("-k", "--keep", dest="keep",
    help="""List of directories which you want to keep, separated by commas. \
        EXAMPLE: exclude_duplicates.py --keep /path/to/directory1,/path/to/directory\ with\ space\ in\ path2""",
    metavar="keep"
)
parser.add_option("-d", "--delimiter", dest="delimiter",
    help="Delimiter of duplicate file groups", metavar="delimiter"
)
parser.add_option("-f", "--file", dest="file",
    help="List of duplicate file groups, separated by delimiter, for example, fdupes or fslint output.", metavar="file"
)

(options, args) = parser.parse_args()
directories_to_keep = options.keep.split(',')
file = options.file
delimiter = options.delimiter

pretty_line = '\n#' + '-' * 35
print '#/bin/bash'
print '#I will protect files in these directories:\n'
for d in directories_to_keep:
    print '# ' + d
print pretty_line

protected_set = set()
group_set = set()

def clean_set(group_set, protected_set, delimiter_line):
    not_protected_set = group_set - protected_set
    while not_protected_set:
        if len(not_protected_set) == 1 and len(protected_set) == 0:
            print '#randomly selected duplicate to keep:\n#rm "%s"' % not_protected_set.pop().strip('\n')
        else:
            print 'rm "%s"' % not_protected_set.pop().strip('\n')
    for i in protected_set: print '#excluded file in protected directory:\n#rm "%s"' % i.strip('\n')
    print '\n#%s' % delimiter_line
file = open(file, 'r')
for line in file.readlines():
    if line.startswith(delimiter):
        clean_set(group_set, protected_set, line)
        group_set, protected_set = set(), set()
    else:
        group_set = group_set|{line}
        for d in directories_to_keep:
            if line.startswith(d): protected_set = protected_set|{line}
else:
    if line: clean_set(group_set, protected_set, line)

В результате remove-duplicate-files-keep-protected.shтолько что созданный нами файл закомментирует все файлы из защищенных каталогов. Откройте этот файл в вашем любимом текстовом редакторе, проверьте, что все в порядке. Тогда запустите это. Вуаля!

Иван Харламов
источник
Я думал об этом, но это не достаточно автоматизировано. глупо, я вызвал потерю данных этим методом, когда имел дело с дубликатами, расположенными в нескольких файловых системах ... нет способа назначить приоритет, учитывая вывод fdupes. в основном мне пришлось бы перелистывать 10000 файлов вручную, чтобы предотвратить потерю данных ... так что, нет, спасибо ... на самом деле, именно потеря данных - это та самая причина, по которой я задал этот вопрос.
ixtmixilix
@ixtmixilix, ну, ручной метод зависит от внимательности пользователя, здесь нет ничего нового. Если вы хотите что-то более автоматизированное, проверьте обновленный ответ выше.
Иван Харламов
2

Как насчет этого?

#!/bin/bash

DUPE_SEARCH_DIR=somedir/
PREFERRED_DIRS=("somedir/subdir1" "somedir/subdir2")
DUPE_FILE=/tmp/`basename $0`_found-duplicates

delete_dupes() {
    while read line ; do
        if [ -n "$line" ] ; then
            matched=false
            for pdir in "${PREFERRED_DIRS[@]}" ; do
                if [[ $line == $pdir/* ]] ; then
                    matched=true
                    break
                fi
            done
            if ! $matched ; then
                rm -v "$line"
            fi
        fi
    done < "$DUPE_FILE"
}

cleanup() {
    rm -f $DUPE_FILE
}

trap cleanup EXIT

# get rid of normal dupes, preserve first & preserve preferred
fdupes -rf "$DUPE_SEARCH_DIR" > $DUPE_FILE
delete_dupes

# get rid of preserve dupes, preserve preferred
fdupes -r "$DUPE_SEARCH_DIR" > "$DUPE_FILE"
delete_dupes
Rynchodon
источник