Esiste un modo per eliminare i duplicati più raffinati di fdupes -rdN?


22

Di recente ho bisogno di eliminare molti duplicati. Sto unendo tre o quattro filesystem e voglio che lo spazio sia utilizzato economicamente. All'inizio, fdupessembrava che fosse lo strumento migliore per il lavoro, ma sto incontrando sempre più limiti.

Considera il comando fdupes -rdN somedirectory/. Questo crea un hash di tutti i file nelle sottodirectory di somedirectory.

E quando incontra duplicati, li elimina, in modo che esista una sola copia di tutto.

Ma cosa succede se voglio conservare somedirectory/subdirectory1/somefilee ci sono, in effetti, quattro duplicati e il programma incontra prima uno dei duplicati? Quindi elimina somedirectory/subdirectory1/somefile, cosa che non voglio.

Voglio essere in grado di specificare, in qualche modo, quali duplicati conservare. E finora, nessuno dei programmi standard per gestire i duplicati (duff, FSLint) sembra consentire l'automazione di quel tipo di comportamento. Preferirei non farmi il mio, quindi è per questo che sto ponendo questa domanda.

Mi piacerebbe poter scrivere qualcosa del genere

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

Stavo cercando la stessa cosa e ho trovato questo superuser.com/a/561207/218922
alexis

Risposte:


5

Mentre la funzionalità che cerchi non è disponibile in magazzino fdupes, ho forkato fdupes (il mio fork si chiama jdupes) e ho aggiunto alcune funzionalità che possono risolvere questo problema in determinate circostanze. Ad esempio, nel caso indicato in cui si desidera conservare somedirectory/subdirectory1/somefilequando si eliminano automaticamente i duplicati (ildN tasti e insieme) e non ci sono file separati immediatamente sotto somedirectory, è jdupespossibile alimentare ciascun percorso di sottodirectory immediato con subdirectory1primo e il -Otasto (che ordina i file per comando -line prima l'ordine dei parametri):

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

Ciò eliminerà automaticamente tutti i file tranne uno in un set duplicato e garantirà che se il set contiene un file somedirectory/subdirectory1sarà il primo, diventando automaticamente il file conservato nel set. Esistono ancora limiti evidenti a questo approccio, come il fatto che un altro duplicato somedirectory/subdirectory1potrebbe essere conservato al posto di quello che si desidera conservare, ma in un buon numero di casi come il tuo, l' jdupesopzione dell'ordine dei parametri come soluzione alternativa è abbastanza buona.

Nel prossimo futuro, ho in programma di aggiungere un sistema di filtro jdupesche consentirà un enorme controllo su inclusione / esclusione di file, conservazione delle -Nazioni e applicazione di tali "stack di filtri" su base globale o per parametro. Questa funzione è estremamente necessaria; Immagino qualcosa del genere per "eliminare automaticamente i duplicati diversi da zero in modo ricorsivo MA preservare sempre così somedirectory/subdirectory1/somefilecom'è":

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


4

Che ne dite di unire i file duplicati insieme? In questo modo lo spazio viene utilizzato solo una volta, ma esistono ancora in tutti i percorsi. Il problema è che i file hardlinked dovrebbero essere modificati in atto (dovrebbero essere modificati solo cancellando il file e ricreandolo con il nuovo contenuto). L'altro approccio consiste nel collegare simbolicamente i file insieme, anche se si ha lo stesso problema di decidere quale sia il file "primario". Questo potrebbe essere fatto con il seguente script (sebbene si noti che questo non gestisce i nomi di file contenenti spazi).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done

1
Usando al jdupesposto fdupestuo puoi semplicemente andare jdupes -nrL somedirectory/che è enormemente più veloce.
Jody Lee Bruchon,

1
Errore di battitura nel collegamento a jdupes. Link utili: github.com/jbruchon/jdupes
Royce Williams

4

Non l'ho visto da nessun'altra parte: dì quello che vuoi è questo. Hai / mnt / cartella-albero-1 / mnt / cartella-albero-2. Non si desidera rimuovere tutti i duplicati, ma se esiste un file nella struttura ad albero 2 ed esiste un file identico nella struttura ad albero 1 con lo stesso percorso e nome esatti, rimuoverlo dalla struttura ad albero 2.

Attenzione: questo è piuttosto conciso e se provi a copiarlo e incollarlo con abilità di shell limitate, fai attenzione.

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

O tutto su una riga:

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

Successivamente, ispezionare ed eseguire rm-v2-dupes.sh


4

Ho avuto la stessa domanda. Se hai molti duplicatifdupes /my/directory/ -rdN mantiene il file con la data di modifica più vecchia o se più file hanno la stessa data di modifica, quella trovata per prima.

Se la data di modifica non è importante per te, puoi touchconservare i file nella directory che desideri conservare. Se lo scegli touchcon la data e l'ora correnti, fdupes -rdNimanterranno quelle con la data corrente. Oppure puoi touchconservare i file con una data precedente a quella che desideri eliminare e utilizzarefdupes -rdN normalmente.

Se è necessario mantenere la data di modifica, sarà necessario utilizzare uno degli altri metodi.


3

Solo per aggiungere una svolta a una risposta precedente. Ho usato il seguente codice più volte, modificando leggermente una risposta precedente con un semplice | grepper isolare la cartella da cui voglio eliminare.

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

Ancora una volta, questo creerà un file sh per eliminare tutti i file elencati, senza righe commentate. Ovviamente puoi comunque modificare il file per commentare specifiche linee / file che vuoi conservare.

Un altro suggerimento per le directory di grandi dimensioni è eseguire fdupes in un file txt, quindi sperimentare | grepe | sedfino a quando non ottengo il risultato desiderato.

`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

Utilizzare sedper creare un file shell che conterrà i comandi commentati per eliminare ciascuno dei file duplicati:

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

Il remove-duplicate-files.shfile risultante che abbiamo appena creato avrà commentato ogni riga. Rimuovi il commento dai file che desideri eliminare. Quindi corri sh remove-duplicate-files.sh. Ecco!

AGGIORNARE

Bene, se non vuoi eliminare i file solo in determinate directory, è semplice come questo :

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

Dove 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)

Il remove-duplicate-files-keep-protected.shfile risultante che abbiamo appena creato avrà commentato tutti i file delle directory protette. Apri questo file nel tuo editor di testo preferito, controlla che sia tutto a posto. Quindi eseguirlo. Voila (sic)!


ci ho pensato, ma non è abbastanza automatizzato. stupidamente, ho causato la perdita di dati con questo metodo quando ho a che fare con duplicati distribuiti su più filesystem ... non c'è modo di assegnare una priorità, dato l'output di fdupes. in pratica avrei dovuto cercare manualmente tra i 10000 file per evitare la perdita di dati ... quindi, no grazie ... in effetti, la perdita di dati è la ragione per cui ho posto questa domanda.
ixtmixilix,

@ixtmixilix, beh, il metodo manuale dipende dall'attenzione dell'utente, ecco niente di nuovo. Se vuoi qualcosa di più automatizzato, controlla una risposta aggiornata sopra.
Ivan Kharlamov il

2

Che ne dici di qualcosa del genere?

#!/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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.