2010-01-21 26 views
27

¿Cómo puedo devolver una lista de archivos que se llaman duplicados, es decir, tienen el mismo nombre pero en diferentes casos que existen en el mismo directorio ?¿Cómo encontrar archivos duplicados con el mismo nombre pero en diferentes casos que existen en el mismo directorio en Linux?

No me importa el contenido de los archivos. Solo necesito saber la ubicación y el nombre de cualquier archivo que tenga un duplicado del mismo nombre.

Ejemplo duplicados:

/www/images/taxi.jpg 
/www/images/Taxi.jpg 

Lo ideal sería que necesitan para buscar todos los archivos de forma recursiva de un directorio base. En el ejemplo anterior, se /www/

+0

lo que si usted tiene mismo nombre pero en minúsculas y todo en diferentes carpetas? ¿Cuál vas a eliminar? – ghostdog74

+1

@ghost: pero en diferentes casos que existen _en la misma carpeta_. – paxdiablo

+0

Como lo señala @paxdiablo, solo me importan los nombres duplicados que existen en la misma carpeta. – Camsoft

Respuesta

36

La otra respuesta es grande, pero en lugar de la secuencia de comandos Perl "en lugar monstruosa" i sugieren

perl -pe 's!([^/]+)$!lc $1!e' 

Qué va a minúsculas sólo la parte de nombre de archivo de la ruta.

Edición 1: De hecho, todo el problema se puede resolver con:

find . | perl -ne 's!([^/]+)$!lc $1!e; print if 1 == $seen{$_}++' 

Datos 3: he encontrado una solución usando sed, clasificar y uniq que también imprimir los duplicados, pero sólo funciona si no hay espacios en blanco en los nombres de archivo:

find . |sed 's,\(.*\)/\(.*\)$,\1/\2\t\1/\L\2,'|sort|uniq -D -f 1|cut -f 1 

Edición 2: Y aquí es un script ya que imprima t él nombra, toma una lista de caminos en stdin, según lo dado por find. No es tan elegante, pero aún así:

#!/usr/bin/perl -w 

use strict; 
use warnings; 

my %dup_series_per_dir; 
while (<>) { 
    my ($dir, $file) = m!(.*/)?([^/]+?)$!; 
    push @{$dup_series_per_dir{$dir||'./'}{lc $file}}, $file; 
} 

for my $dir (sort keys %dup_series_per_dir) { 
    my @all_dup_series_in_dir = grep { @{$_} > 1 } values %{$dup_series_per_dir{$dir}}; 
    for my $one_dup_series (@all_dup_series_in_dir) { 
     print "$dir\{" . join(',', sort @{$one_dup_series}) . "}\n"; 
    } 
} 
+5

+1. Recomiendo encarecidamente aceptar * este * como la respuesta (en lugar de mi respuesta actualmente aceptada). Es mucho más elegante. Mi versión final es una monstruosidad ya que llegué desde un punto de vista de canalización y tuve que agregar Perl para resolver un problema con tr. Esta respuesta es una prueba positiva de que a menudo se pueden obtener mejores soluciones dando un paso atrás y comenzando de nuevo. – paxdiablo

+2

Y use "find -type f" si lo quiere restringido a archivos normales (sin directorios). – paxdiablo

+0

Sería útil si el script mostrara ambos archivos ofensivos. – Soviut

32

Probar:

ls -1 | tr '[A-Z]' '[a-z]' | sort | uniq -c | grep -v " 1 " 

simple, en realidad :-) ¿No son maravillosas tuberías bestias?

El ls -1 le da los archivos uno por línea, el tr '[A-Z]' '[a-z]' convierte todo en mayúsculas a minúsculas, los sort tipo ellos (sorprendentemente), uniq -c elimina las repeticiones posteriores de las líneas duplicadas, mientras que le da un recuento, así y, por último, la grep -v " 1 " quita esas líneas donde el recuento era uno.

Cuando ejecuto esto en un directorio con un "duplicado" (He copiado qq-qQ), me sale:

2 qq 

Para el "este directorio y cada subdirectorio" versión, basta con sustituir ls -1 con find . o find DIRNAME si desea un punto de inicio de directorio específico (DIRNAME es el nombre del directorio que desea usar).

Esto devuelve (para mí):

2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3 
2 ./.gconf/system/gstreamer/0.10/audio/profiles/mp3/%gconf.xml 
2 ./.gnome2/accels/blackjack 
2 ./qq 

que son causados ​​por:

pax> ls -1d .gnome2/accels/[bB]* .gconf/system/gstreamer/0.10/audio/profiles/[mM]* [qQ]? 
.gconf/system/gstreamer/0.10/audio/profiles/mp3 
.gconf/system/gstreamer/0.10/audio/profiles/MP3 
.gnome2/accels/blackjack 
.gnome2/accels/Blackjack 
qq 
qQ 

Actualización:

En realidad, en una reflexión más profunda, la tr se minúsculas todas componentes de la ruta de modo que ambos

/a/b/c 
/a/B/c 

serán considerados duplicados pesar de que están en diferentes directorios.

Si sólo desea duplicados dentro de un único directorio para mostrar como un partido, se puede utilizar el (más monstruosa):

perl -ne ' 
    chomp; 
    @flds = split (/\//); 
    $lstf = $f[-1]; 
    $lstf =~ tr/A-Z/a-z/; 
    for ($i =0; $i ne $#flds; $i++) { 
     print "$f[$i]/"; 
    }; 
    print "$x\n";' 

en lugar de:

tr '[A-Z]' '[a-z]' 

Lo que hace es solo minúscula la porción final del nombre de ruta en lugar de todo.Además, si solo desea archivos regulares (sin directorios, FIFO, etc.), use find -type f para restringir lo que se devuelve.

+0

Wow. Ese es un comando impresionante. No puedo imaginarme alguna vez poder hacer eso en Windows. Me encanta * nix. Muchas gracias. – Camsoft

+0

Puede hacerlo en Windows muy bien. Consígase una copia de Cygwin o MinGW y disfrute :-) – paxdiablo

+0

Pero no puede hacerlo de la caja. – Camsoft

4

creo

ls | sort -f | uniq -i -d 

es más simple, más rápido y le dará el mismo resultado

+0

Sí, para el directorio actual. Pero ¿qué hay de los subdirectorios? Tenga en cuenta que solo puede ignorar mayúsculas y minúsculas para el nombre base, no para la ruta completa. –

+0

En Mac OSX, si tiene caracteres no ASCII, puede que necesite establecer la codificación de caracteres con 'exportar LC_ALL = 'C'' – Turadg

+0

para subdirectorios agregar el -R cambiar a ls –

2

El seguimiento de la respuesta de mpez0, para detectar de forma recursiva justo reemplace "ls" por "buscar". El único problema que veo con esto es que si este es un directorio que se está duplicando, entonces tiene 1 entrada para cada archivo en este directorio. Se requiere algo del cerebro humano para tratar el resultado de esto.

Pero de todos modos, no está eliminando automáticamente estos archivos, ¿o sí?

find . | sort -f | uniq -i -d 
+0

Esto es posiblemente un comentario más que una respuesta, parece hacer preguntas aclaratorias. – vgoff

2

Esta es una aplicación de línea de comandos poco agradable llamada findsn que se obtiene si se compila fslint que el paquete deb no incluye.

encontrará cualquier archivo con el mismo nombre, y es muy rápido y puede manejar diferentes casos.

/findsn --help 
find (files) with duplicate or conflicting names. 
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...] 

Si no hay argumentos se suministran los $ PATH es buscada para cualquier o conflictivos archivos redundantes.

-A reports all aliases (soft and hard links) to files. 
    If no path(s) specified then the $PATH is searched. 

Si único camino (s) especificado, entonces se comprueba su duplicado llamado archivos. Puede calificar esto con -C para ignorar el caso en esta búsqueda. Calificar con -c es más restrictivo que solo los archivos (o directorios) en el mismo directorio cuyos nombres difieren solo en caso de que se denuncien. I.E. -c marcará los directorios de los archivos & que entrarán en conflicto si se transfiere a un sistema de archivos que no distingue entre mayúsculas y minúsculas. Tenga en cuenta si se especificó -c o -C y ninguna ruta especificada (s) se asume el directorio actual.

2

Aquí es un ejemplo de cómo encontrar todos los archivos jar duplicados:

find . -type f -printf "%f\n" -name "*.jar" | sort -f | uniq -i -d 

Reemplazar *.jar con cualquier tipo de archivos duplicados que busca.

1

Aquí hay un script que funcionó para mí (yo no soy el autor). la discusión original y se puede encontrar aquí: http://www.daemonforums.org/showthread.php?t=4661

#! /bin/sh 

# find duplicated files in directory tree 
# comparing by file NAME, SIZE or MD5 checksum 
# -------------------------------------------- 
# LICENSE(s): BSD/CDDL 
# -------------------------------------------- 
# vermaden [AT] interia [DOT] pl 
# http://strony.toya.net.pl/~vermaden/links.htm 

__usage() { 
    echo "usage: $(basename ${0}) OPTION DIRECTORY" 
    echo " OPTIONS: -n check by name (fast)" 
    echo "   -s check by size (medium)" 
    echo "   -m check by md5 (slow)" 
    echo "   -N same as '-n' but with delete instructions printed" 
    echo "   -S same as '-s' but with delete instructions printed" 
    echo "   -M same as '-m' but with delete instructions printed" 
    echo " EXAMPLE: $(basename ${0}) -s /mnt" 
    exit 1 
    } 

__prefix() { 
    case $(id -u) in 
    (0) PREFIX="rm -rf" ;; 
    (*) case $(uname) in 
      (SunOS) PREFIX="pfexec rm -rf" ;; 
      (*)  PREFIX="sudo rm -rf" ;; 
     esac 
     ;; 
    esac 
    } 

__crossplatform() { 
    case $(uname) in 
    (FreeBSD) 
     MD5="md5 -r" 
     STAT="stat -f %z" 
     ;; 
    (Linux) 
     MD5="md5sum" 
     STAT="stat -c %s" 
     ;; 
    (SunOS) 
     echo "INFO: supported systems: FreeBSD Linux" 
     echo 
     echo "Porting to Solaris/OpenSolaris" 
     echo " -- provide values for MD5/STAT in '$(basename ${0}):__crossplatform()'" 
     echo " -- use digest(1) instead for md5 sum calculation" 
     echo "  $ digest -a md5 file" 
     echo " -- pfexec(1) is already used in '$(basename ${0}):__prefix()'" 
     echo 
     exit 1 
    (*) 
     echo "INFO: supported systems: FreeBSD Linux" 
     exit 1 
     ;; 
    esac 
    } 

__md5() { 
    __crossplatform 
    :> ${DUPLICATES_FILE} 
    DATA=$(find "${1}" -type f -exec ${MD5} {} ';' | sort -n) 
    echo "${DATA}" \ 
    | awk '{print $1}' \ 
    | uniq -c \ 
    | while read LINE 
     do 
     COUNT=$(echo ${LINE} | awk '{print $1}') 
     [ ${COUNT} -eq 1 ] && continue 
     SUM=$(echo ${LINE} | awk '{print $2}') 
     echo "${DATA}" | grep ${SUM} >> ${DUPLICATES_FILE} 
     done 

    echo "${DATA}" \ 
    | awk '{print $1}' \ 
    | sort -n \ 
    | uniq -c \ 
    | while read LINE 
     do 
     COUNT=$(echo ${LINE} | awk '{print $1}') 
     [ ${COUNT} -eq 1 ] && continue 
     SUM=$(echo ${LINE} | awk '{print $2}') 
     echo "count: ${COUNT} | md5: ${SUM}" 
     grep ${SUM} ${DUPLICATES_FILE} \ 
      | cut -d ' ' -f 2-10000 2> /dev/null \ 
      | while read LINE 
      do 
       if [ -n "${PREFIX}" ] 
       then 
       echo " ${PREFIX} \"${LINE}\"" 
       else 
       echo " ${LINE}" 
       fi 
      done 
     echo 
     done 
    rm -rf ${DUPLICATES_FILE} 
    } 

__size() { 
    __crossplatform 
    find "${1}" -type f -exec ${STAT} {} ';' \ 
    | sort -n \ 
    | uniq -c \ 
    | while read LINE 
     do 
     COUNT=$(echo ${LINE} | awk '{print $1}') 
     [ ${COUNT} -eq 1 ] && continue 
     SIZE=$(echo ${LINE} | awk '{print $2}') 
     SIZE_KB=$(echo ${SIZE}/1024 | bc) 
     echo "count: ${COUNT} | size: ${SIZE_KB}KB (${SIZE} bytes)" 
     if [ -n "${PREFIX}" ] 
     then 
      find ${1} -type f -size ${SIZE}c -exec echo " ${PREFIX} \"{}\"" ';' 
     else 
      # find ${1} -type f -size ${SIZE}c -exec echo " {} " ';' -exec du -h " {}" ';' 
      find ${1} -type f -size ${SIZE}c -exec echo " {} " ';' 
     fi 
     echo 
     done 
    } 

__file() { 
    __crossplatform 
    find "${1}" -type f \ 
    | xargs -n 1 basename 2> /dev/null \ 
    | tr '[A-Z]' '[a-z]' \ 
    | sort -n \ 
    | uniq -c \ 
    | sort -n -r \ 
    | while read LINE 
     do 
     COUNT=$(echo ${LINE} | awk '{print $1}') 
     [ ${COUNT} -eq 1 ] && break 
     FILE=$(echo ${LINE} | cut -d ' ' -f 2-10000 2> /dev/null) 
     echo "count: ${COUNT} | file: ${FILE}" 
     FILE=$(echo ${FILE} | sed -e s/'\['/'\\\['/g -e s/'\]'/'\\\]'/g) 
     if [ -n "${PREFIX}" ] 
     then 
      find ${1} -iname "${FILE}" -exec echo " ${PREFIX} \"{}\"" ';' 
     else 
      find ${1} -iname "${FILE}" -exec echo " {}" ';' 
     fi 
     echo 
     done 
    } 

# main() 

[ ${#} -ne 2 ] && __usage 
[ ! -d "${2}" ] && __usage 

DUPLICATES_FILE="/tmp/$(basename ${0})_DUPLICATES_FILE.tmp" 

case ${1} in 
    (-n)   __file "${2}" ;; 
    (-m)   __md5 "${2}" ;; 
    (-s)   __size "${2}" ;; 
    (-N) __prefix; __file "${2}" ;; 
    (-M) __prefix; __md5 "${2}" ;; 
    (-S) __prefix; __size "${2}" ;; 
    (*) __usage ;; 
esac 

Si el comando find no está trabajando para usted, usted puede tener que cambiarlo. Por ejemplo

OLD : find "${1}" -type f | xargs -n 1 basename 
NEW : find "${1}" -type f -printf "%f\n" 
1

Se puede utilizar:

find -type f -exec readlink -m {} \; | gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}' | uniq -c 

Dónde:

  • find -type f
    recursividad de impresión ruta completa de todos los archivos. ruta absoluta

  • -exec readlink -m {} \;
    de archivo get

  • gawk 'BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'
    sustituir la a minúscula

  • uniq -c
    único camino, la salida de todos los -c nombre del conde de duplicado.

-1

acabo de utilizar fdupes en CentOS para limpiar su conjunto buncha archivos duplicados ...

yum install fdupes 
0

Un poco tarde para esto, pero aquí está la versión Fui con:

find . -type f | awk -F/ '{print $NF}' | sort -f | uniq -i -d 

Aquí estamos utilizando:

  1. find - encontrar todos los archivos del directorio actual
  2. awk - quitar la parte de ruta de archivo del nombre de archivo
  3. sort - caso especie insensible
  4. uniq - encontrar los duplicados de lo que lo hace a través de la tubería

(Inspirado por @ mpez0 respuesta, y @SimonDowdle s comentario en @paxdiablo respuesta.)

0

Puede comprobar duplicados en un directorio dado con awk GNU:

gawk 'BEGINFILE {if ((seen[tolower(FILENAME)]++)) print FILENAME; nextfile}' * 

Este utiliza BEGINFILE para realizar alguna acción antes de pasar y la lectura de un archivo. En este caso, realiza un seguimiento de los nombres que han aparecido en una matriz seen[] cuyos índices son los nombres de los archivos en minúscula.

Si ya ha aparecido un nombre, no importa su caso, lo imprime. De lo contrario, simplemente salta al siguiente archivo.


ver un ejemplo:

$ tree 
. 
├── bye.txt 
├── hello.txt 
├── helLo.txt 
├── yeah.txt 
└── YEAH.txt 

0 directories, 5 files 
$ gawk 'BEGINFILE {if ((a[tolower(FILENAME)]++)) print FILENAME; nextfile}' * 
helLo.txt 
YEAH.txt 
Cuestiones relacionadas