Recentemente ho la necessità di eliminare molti duplicati. Sto unendo tre o quattro filesystem e voglio che lo spazio venga utilizzato in modo economico. Allinizio, fdupes
sembrava lo strumento migliore per il lavoro, ma sono sempre più incappato in limitazioni.
Considera il comando fdupes -rdN somedirectory/
. Questo crea un hash di tutti i file nelle sottodirectory di una directory.
E quando incontra dei duplicati, li elimina, in modo che ci sia solo una copia di tutto.
Ma cosa succede se voglio mantenere somedirectory/subdirectory1/somefile
e ci sono, infatti, quattro duplicati e il programma incontra per primo uno dei duplicati? Quindi elimina somedirectory/subdirectory1/somefile
, che non voglio.
Voglio essere in grado di specificare, in qualche modo, quali duplicati mantenere. E finora, nessuno dei programmi standard per la gestione dei duplicati (duff, FSLint) sembra consentire lautomazione di quel tipo di comportamento. Preferirei non eseguire il mio, quindi è per questo che sto facendo questa domanda.
Vorrei poter scrivere qualcosa del tipo
killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
Commenti
- Ero cercando la stessa cosa e ho trovato questa superuser.com/a/561207/218922
risposta
Sebbene la funzionalità che cerchi non sia disponibile in stock fdupes
, ho biforcato fdupes
(il mio fork si chiama jdupes
) e ha aggiunto alcune funzionalità che possono risolvere questo problema in determinate circostanze. Per esempio, nel caso indicato in cui desideri mantenere somedirectory/subdirectory1/somefile
quando si eliminano automaticamente i duplicati (d
e N
si scambiano insieme) e non ci sono file separati immediatamente sotto somedirectory
, jdupes
può essere alimentato con ogni percorso di sottodirectory immediato con subdirectory1
prima e -O
opzione (che ordina prima i file in base allordine dei parametri della riga di comando):
jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3
Questo eliminerà automaticamente tutti i file tranne uno in un set duplicato e garantirà che se il set contiene un file in somedirectory/subdirectory1
sarà il primo, diventando così automaticamente il file conservato nel set . Esistono ancora limiti evidenti a questo approccio, come il fatto che un altro duplicato in somedirectory/subdirectory1
potrebbe essere conservato al posto di quello che si desidera mantenere, ma in un buon numero di casi come il tuo, lopzione jdupes
per lordine dei parametri come soluzione alternativa è abbastanza buona.
Nel prossimo futuro, ho intenzione di aggiungere un sistema di filtri a jdupes
che consentirà un enorme controllo sullinclusione / esclusione di file, la conservazione per -N
azioni e lapplicazione di tali “stack di filtri” su un -parametro di base. Questa caratteristica è assolutamente necessaria; Immagino qualcosa di simile per “eliminare automaticamente i duplicati diversi da zero in modo ricorsivo MA preservare sempre somedirectory/subdirectory1/somefile
così comè”:
jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/
Risposta
Non lho visto da nessunaltra parte: dì quello che vuoi è questo. Hai / mnt / folder-tree-1 / mnt / folder-tree-2. Non vuoi rimuovere tutti i duplicati, ma se esiste un file in tree-2 e un file identico esiste in tree-1 con lo stesso identico percorso e nome, rimuovilo dallalbero-2.
Attenzione: questo è abbastanza 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, ispeziona ed esegui rm-v2-dupes.sh
Risposta
Che ne dici di collegare insieme i file duplicati? In questo modo lo spazio viene utilizzato solo una volta, ma esistono ancora in tutti i percorsi. Il problema è che i file hardlink devono essere modificati in posizione (dovrebbero essere modificati solo eliminando il file e ricreandolo con il nuovo contenuto). Laltro approccio consiste nel creare un collegamento simbolico tra i file, sebbene tu abbia lo stesso problema di decidere quale sia il file “primario”. Questo potrebbe essere fatto con il seguente script (anche se tieni presente 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
Commenti
- Utilizzo di
jdupes
invece difdupes
puoi semplicemente usarejdupes -nrL somedirectory/
che è molto più veloce. - Errore di battitura nel link a jdupes. Link di convenienza: github.com/jbruchon/jdupes
Risposta
Avevo la stessa domanda.Se hai molti duplicati, fdupes /my/directory/ -rdN
conserva il file con la data di modifica più vecchia, o se diversi file hanno la stessa data di modifica, quello trovato per primo.
Se la data di modifica non è importante per te, puoi touch
i file nella directory che desideri conservare. Se scegli di touch
con la data e lora correnti, fdupes -rdNi
manterrà quelle con la data corrente. Oppure puoi touch
conservare i file con una data precedente a quella che desideri eliminare e utilizzare fdupes -rdN
normalmente.
Se è necessario mantenere la data di modifica, sarà necessario utilizzare uno degli altri metodi.
Risposta
Solo per aggiungere una svolta a una risposta precedente. Ho utilizzato il seguente codice più volte, modificando leggermente una risposta precedente con un semplice | grep
per isolare la cartella da cui desidero eliminare.
`fdupes -r -n -S /directory | grep /delete-from-directory | sed -r "s/^/rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh`
Di nuovo, questo creerà un file sh per eliminare tutti i file elencati, nessuna riga commentata. Ovviamente puoi ancora modificare il file per commentare righe / file specifici che vuoi mantenere.
Un altro suggerimento per directory di grandi dimensioni è eseguire fdupes in un file txt, quindi provare | grep
e | sed
finché non ottengo il risultato che desidero.
`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`
Risposta
Utilizza sed
per creare un file di shell che conterrà comandi commentati per eliminare ciascuno dei tuoi file duplicati:
fdupes -r -n -S /directory | sed -r "s/^/#rm \"/" | sed -r "s/$/\"/" >remove-duplicate-files.sh
remove-duplicate-files.sh
fil Ogni riga che abbiamo appena creato avrà ogni riga commentata. Rimuovi il commento dai file che desideri eliminare. Quindi esegui sh remove-duplicate-files.sh
. Ecco!
AGGIORNAMENTO
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)
remove-duplicate-files-keep-protected.sh
che abbiamo appena creato avrà tutti i file dalle directory protette commentati. Apri questo file nel tuo editor di testo preferito, controlla che sia tutto a posto. Quindi eseguilo. Voila (sic)!
Commenti
- Ci ho pensato, ma ‘ non è abbastanza automatizzato. Stupidamente, ho causato la perdita di dati con questo metodo quando gestire i duplicati spaziati su più filesystem … ‘ non è possibile assegnare una priorità, dato loutput di fdup es. fondamentalmente avrei dovuto setacciare manualmente 10000 file per evitare la perdita di dati … quindi, no grazie … in effetti, quella perdita di dati è proprio la ragione per cui ho posto questa domanda.
- @ixtmixilix, beh, il metodo manuale dipende dallattenzione dellutente, qui ‘ non cè niente di nuovo. Se vuoi qualcosa di più automatizzato, controlla una risposta aggiornata sopra.
Risposta
Che ne dici di qualcosa di simile?
#!/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
Risposta
A partire da Apple Filesystem (APFS), unaltra soluzione è conservare i file, deduplicarli e non avere alcun impatto sullutilizzo del disco. Vedi Sostituzione di file duplicati esistenti su APFS con cloni