Recientemente, tuve la necesidad de eliminar muchos duplicados. Estoy fusionando tres o cuatro sistemas de archivos y quiero que el espacio se utilice de forma económica. Al principio, fdupes
parecía la mejor herramienta para el trabajo, pero cada vez tengo más limitaciones.
Considere el comando fdupes -rdN somedirectory/
. Esto hace un hash de todos los archivos en los subdirectorios de algún directorio.
Y cuando encuentra duplicados, los borra, por lo que solo hay una copia de todo.
Pero, ¿y si quiero mantener somedirectory/subdirectory1/somefile
y hay, de hecho, cuatro duplicados, y el programa encuentra uno de los duplicados primero? Luego borra somedirectory/subdirectory1/somefile
, que no quiero.
Quiero poder especificar, de alguna forma, qué duplicados conservar. Y hasta ahora, ninguno de los programas estándar para tratar con duplicados (duff, FSLint) parecen permitir la automatización de ese tipo de comportamiento. Preferiría no lanzar el mío propio, por eso es que estoy haciendo esta pregunta.
Me gustaría poder escribir algo como
killdupes -rdN --keep=filesin,somedirectories,separated,by,commas somedirectory/
Comentarios
- Estaba buscando lo mismo y encontré esta superuser.com/a/561207/218922
Respuesta
Si bien la funcionalidad que busca no está disponible en stock fdupes
, bifurqué fdupes
(mi bifurcación se llama jdupes
) y agregué algunas características que pueden resolver este problema en ciertas circunstancias. Para ejemplo, en el caso indicado en el que desea mantener somedirectory/subdirectory1/somefile
cuando se borran automáticamente los duplicados (los interruptores d
y N
juntos) y no hay archivos separados inmediatamente debajo de somedirectory
, jdupes
se puede alimentar a cada ruta de subdirectorio inmediata con subdirectory1
primero y la -O
conmutador (que ordena los archivos por orden de parámetros de la línea de comandos primero):
jdupes -nrdNO somedirectory/subdirectory1 somedirectory/subdirectory2 somedirectory/subdirectory3
Este eliminará automáticamente todos los archivos menos uno en un conjunto duplicado y garantizará que si el conjunto contiene un archivo en somedirectory/subdirectory1
, será el primero, convirtiéndose automáticamente en el archivo preservado en el conjunto . Todavía existen límites evidentes para este enfoque, como el hecho de que se podría conservar otro duplicado en somedirectory/subdirectory1
en lugar del que deseaba conservar, pero en un buen número de casos como el suyo, la opción de orden de parámetro jdupes
como solución alternativa es suficientemente buena.
En un futuro cercano, planeo agregar un sistema de filtrado a jdupes
que permitirá una gran cantidad de control sobre la inclusión / exclusión de archivos, la preservación de -N
acciones y la aplicación de tales «pilas de filtros» en un global o por -Base de parámetros. Esta característica es muy necesaria; Imagino algo como esto para «eliminar automáticamente los duplicados distintos de cero de forma recursiva, PERO siempre conservar somedirectory/subdirectory1/somefile
tal cual»:
jdupes -nrdN --filter=preserve:somedirectory/subdirectory1/somefile somedirectory/
Respuesta
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 exactamente el mismo ruta y nombre, elimínelo del árbol-2.
Advertencia: esto es bastante conciso 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
Respuesta
¿Qué hay de vincular los archivos duplicados juntos? De esa manera, el espacio solo se usa una vez, pero aún existen en todos los caminos. El problema con esto es que los archivos con vínculos fijos deben modificarse en su lugar (solo deben modificarse eliminando el archivo y recreándolo con el nuevo contenido). El otro enfoque es vincular simbólicamente los archivos, aunque tiene el mismo problema de decidir cuál es el archivo «principal». Esto se puede hacer con el siguiente script (aunque tenga en cuenta que esto no maneja nombres de archivo que contengan espacios).
fdupes --quiet --recurse --sameline somedirectory/ | while read SOURCE DESTS; do for DEST in $DESTS; do ln -f $SOURCE $DEST done done
Comentarios
- Usando
jdupes
en lugar defdupes
simplemente puede ir ajdupes -nrL somedirectory/
que es muchísimo más rápido. - Error tipográfico en el enlace a jdupes. Enlace de conveniencia: github.com/jbruchon/jdupes
Responder
Tenía la misma pregunta.Si tiene muchos duplicados fdupes /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 que se encontró primero.
Si la fecha de modificación no es importante para usted, puede touch
los archivos en el directorio que desea conservar. Si elige touch
con la fecha y hora actuales, entonces fdupes -rdNi
mantendrá las que tengan la fecha actual. O puede touch
conservar los archivos con una fecha anterior a la de los que desea eliminar y usar fdupes -rdN
como de costumbre.
Si necesita mantener la fecha de modificación, deberá utilizar uno de los otros métodos.
Responder
Solo para agregar un giro a una respuesta anterior. He utilizado el siguiente código varias veces, modificando ligeramente una respuesta anterior con un simple | grep
para 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`
De nuevo, 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.
Otra sugerencia para directorios grandes es ejecutar fdupes en un archivo txt, luego experimentar con | grep
y | sed
hasta 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`
Responder
Utilice sed
para 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.sh
fil El que acabamos de crear tendrá cada línea comentada. Descomente los archivos que desea eliminar. Luego, ejecute 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.py
es:
#/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.sh
archivo 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. Luego ejecútelo. ¡Voila (sic)!
Comentarios
- Pensé en esto, pero ‘ no está lo suficientemente automatizado. Estúpidamente, causé la pérdida de datos con este método cuando lidiando con duplicados espaciados en múltiples sistemas de archivos … no hay ‘ s no hay forma de asignar una prioridad, dada la salida de fdup es. Básicamente, habría tenido que rastrear 10000 archivos a mano para evitar la 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, bueno, el método manual depende de la atención del usuario, aquí ‘ no es nada nuevo. Si desea algo más automatizado, consulte una respuesta actualizada anterior.
Respuesta
¿Qué pasa con 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
Respuesta
A partir de Apple Filesystem (APFS), otra solución es mantener los archivos, deduplicarlos y no tener ningún impacto en el uso del disco. Ver Reemplazar archivos duplicados existentes en APFS con clones