Чи є спосіб видалення дублікатів більш вдосконалених, ніж fdupes -rdN?


22

Останнім часом у мене є необхідність видалити багато дублікатів. Я зливаю три-чотири файлові системи, і хочу, щоб простір було економно використане. Спочатку fdupesздавалося, що це найкращий інструмент для роботи, але я все більше стикаюся з обмеженнями.

Розглянемо команду fdupes -rdN somedirectory/. Це робить хеш усіх файлів у підкаталогах сомедиректорії.

І коли він стикається з дублікатами, він видаляє їх, так що є лише одна копія всього.

Але що робити, якщо я хочу зберегти, somedirectory/subdirectory1/somefileа насправді є чотири дублікати, і програма спочатку стикається з одним із дублікатів? Потім він видаляє somedirectory/subdirectory1/somefile, чого я не хочу.

Я хочу якось уточнити, які дублікати зберігати. І поки що жодна зі стандартних програм поводження з дублікатами (duff, FSLint), здається, не дозволяє автоматизувати таку поведінку. Я вважаю за краще не прокручувати свою, тому я задаю це питання.

Мені б хотілося написати щось подібне

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/

Я шукав те саме, і знайшов це superuser.com/a/561207/218922
alexis

Відповіді:


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

1
Використання jdupesзамість fdupesви можете просто піти , jdupes -nrL somedirectory/які масово швидше.
Джоді Лі Брюшон

1
Друкуйте за посиланням на jdupes. Зручність: посилання: github.com/jbruchon/jdupes
Ройс Вільямс

4

Я більше ніде цього не бачив: Скажи, що ти хочеш, це таке. У вас / mnt / папка-дерево-1 / mnt / папка-дерево-2. Ви не хочете видаляти кожен дуп, але якщо файл існує у дереві-2, а ідентичний файл існує у дереві-1 з точно таким же шляхом та назвою, видаліть його з дерева-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як звичайну.

Якщо вам потрібно зберегти дату зміни, вам знадобиться скористатися одним із інших методів.


3

Просто для додання повороту до попередньої відповіді. Я використовував наступний код кілька разів, трохи змінивши попередню відповідь простим, | 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`

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створеному нами файлі, який ми тільки що створили, будуть коментуватися всі файли із захищених каталогів. Відкрийте цей файл у вашому улюбленому текстовому редакторі, перевірте, чи все в порядку. Потім запустіть його. Вуаля (sic)!


Я думав про це, але це недостатньо автоматизовано. по-дурному, я спричинив втрату даних цим методом при роботі з дублікатами, розміщеними в декількох файлових системах ... немає можливості призначити пріоритет, враховуючи вихід 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.