¿Hay alguna forma de eliminar duplicados más refinados que fdupes -rdN?

22

Recientemente tengo la necesidad de eliminar muchos duplicados. Estoy fusionando tres o cuatro sistemas de archivos, y quiero que el espacio se use económicamente. Al principio, fdupesparecía que era la mejor herramienta para el trabajo, pero cada vez tengo más limitaciones.

Considera el comando fdupes -rdN somedirectory/. Esto hace un hash de todos los archivos en los subdirectorios de somedirectory.

Y cuando encuentra duplicados, los elimina, de modo que solo hay una copia de todo.

Pero, ¿qué pasa si quiero mantener somedirectory/subdirectory1/somefiley, de hecho, hay cuatro duplicados, y el programa encuentra uno de los duplicados primero? Luego se elimina somedirectory/subdirectory1/somefile, lo que no quiero.

Quiero poder especificar, de alguna manera, qué duplicados mantener. Y hasta ahora, ninguno de los programas estándar para tratar con duplicados (duff, FSLint) parece permitir la automatización de ese tipo de comportamiento. Prefiero no rodar el mío, así que por eso estoy haciendo esta pregunta.

Me gustaría poder escribir algo como

killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
ixtmixilix
fuente
Estaba buscando lo mismo y encontré este superuser.com/a/561207/218922
alexis

Respuestas:

5

Si bien la funcionalidad que busca no está disponible en stock fdupes, bifurqué fdupes (se llama mi fork jdupes) y agregué algunas características que pueden resolver este problema en ciertas circunstancias. Por ejemplo, en el caso indicado en el que desea mantener somedirectory/subdirectory1/somefilecuando se eliminan automáticamente los duplicados (el dy Ncambia juntos) y no hay archivos separados inmediatamente debajo somedirectory, jdupesse puede alimentar cada ruta de subdirectorio inmediato con subdirectory1primero y el -Ointerruptor (que clasifica los archivos por comando orden de parámetros de línea primero):

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

Esto eliminará automáticamente todos los archivos excepto uno en un conjunto duplicado y garantizará que si el conjunto contiene un archivo somedirectory/subdirectory1será el primero, convirtiéndose automáticamente en el archivo preservado en el conjunto. Todavía hay límites evidentes para este enfoque, como el hecho de que somedirectory/subdirectory1podría conservarse otro duplicado en lugar del que desea conservar, pero en un buen número de casos como el suyo, la jdupesopción de orden de parámetros como solución alternativa es lo suficientemente buena.

En un futuro próximo, planeo agregar un sistema de filtrado jdupesque permita un gran control sobre la inclusión / exclusión de archivos, la preservación de -Nacciones y la aplicación de tales "pilas de filtros" en forma global o por parámetro. Esta característica es muy necesaria; Imagino algo como esto para "eliminar automáticamente los duplicados distintos de cero de forma recursiva PERO siempre preservarlo somedirectory/subdirectory1/somefiletal cual":

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

Jody Lee Bruchon
fuente
4

¿Qué hay de vincular los archivos duplicados juntos? De esa manera, el espacio solo se usa una vez, pero todavía existen en todos los caminos. El problema con esto es que los archivos enlazados deben modificarse en su lugar (solo deben modificarse eliminando el archivo y recreándolo con el nuevo contenido). El otro enfoque es vincular los archivos juntos, aunque tiene el mismo problema de decidir cuál es el archivo "primario". Esto podría hacerse con el siguiente script (aunque tenga en cuenta que esto no maneja los nombres de archivo que contienen espacios).

fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do
    for DEST in $DESTS; do
        ln -f $SOURCE $DEST
    done
done
mgorven
fuente
1
Usando en jdupeslugar de fdupesusted simplemente puede ir, jdupes -nrL somedirectory/que es masivamente más rápido.
Jody Lee Bruchon
1
Error tipográfico en el enlace a jdupes. Enlace de conveniencia: github.com/jbruchon/jdupes
Royce Williams
4

No vi este en ningún otro lugar: di lo que quieres es esto. Tienes / mnt / folder-tree-1 / mnt / folder-tree-2. No desea eliminar todos los duplicados, pero si existe un archivo en el árbol-2 y existe un archivo idéntico en el árbol-1 con la misma ruta y nombre, elimínelo del árbol-2.

Advertencia: esto es bastante breve y si intenta copiar y pegar esto con habilidades limitadas de shell, tenga cuidado.

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 todo en una línea:

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

Luego, inspeccione y ejecute rm-v2-dupes.sh

Gaute Lund
fuente
4

Tenía la misma pregunta. Si tienes muchos duplicadosfdupes /my/directory/ -rdN mantiene el archivo con la fecha de modificación más antigua, o si varios archivos tienen la misma fecha de modificación, entonces el primero se encuentra.

Si la fecha de modificación no es importante para usted, puede touchguardar los archivos en el directorio que desea conservar. Si los eliges touchcon la fecha y hora actuales fdupes -rdNi, mantendrás las que tengan la fecha actual. O puede touchguardar los archivos con una fecha anterior a la de los que desea eliminar y usar de fdupes -rdNforma normal.

Si necesita mantener la fecha de modificación, deberá usar uno de los otros métodos.

pheon
fuente
3

Solo para agregar un giro a una respuesta anterior. He usado el siguiente código varias veces, modificando ligeramente una respuesta anterior con un simple | greppara aislar la carpeta de la que quiero eliminar.

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

Nuevamente, esto creará un archivo sh para eliminar todos los archivos enumerados, sin líneas comentadas. Por supuesto, aún puede editar el archivo para comentar líneas / archivos específicos que desea conservar.

Otro consejo para directorios grandes es ejecutar fdupes en un archivo txt, luego experimentar con | grepy | sedhasta obtener el resultado que quiero.

`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
fuente
2

Úselo sedpara crear un archivo de shell que contendrá comandos comentados para eliminar cada uno de sus archivos duplicados:

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

El remove-duplicate-files.sharchivo resultante que acabamos de crear tendrá cada línea comentada. Descomente los archivos que desea eliminar. Entonces corre sh remove-duplicate-files.sh. Voila!

ACTUALIZAR

Bueno, si no desea eliminar archivos solo en ciertos directorios, es tan simple como esto :

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

Donde exclude_duplicates.pyes:

#/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)

El remove-duplicate-files-keep-protected.sharchivo resultante que acabamos de crear tendrá todos los archivos de directorios protegidos comentados. Abra este archivo en su editor de texto favorito, verifique que todo esté bien. Entonces ejecútalo. Voila (sic)!

Ivan Kharlamov
fuente
Pensé en esto, pero no está lo suficientemente automatizado. estúpidamente, causé la pérdida de datos con este método al tratar con duplicados espaciados en múltiples sistemas de archivos ... no hay forma de asignar una prioridad, dada la salida de fdupes. básicamente habría tenido que rastrear 10000 archivos a mano para evitar esa pérdida de datos ... así que no, gracias ... de hecho, esa pérdida de datos es la razón por la que hice esta pregunta.
ixtmixilix
@ixtmixilix, bueno, el método manual depende de la atención del usuario, aquí no hay nada nuevo. Si desea algo más automatizado, consulte una respuesta actualizada más arriba.
Ivan Kharlamov
2

¿Qué tal algo como esto?

#!/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
fuente