linux - fotos - fslint
¿Cómo encontrar archivos duplicados con el mismo nombre pero en diferentes casos que existen en el mismo directorio en Linux? (11)
¿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 ?
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 de duplicados:
/www/images/taxi.jpg
/www/images/Taxi.jpg
Idealmente, necesito buscar todos los archivos recursivamente desde un directorio base. En el ejemplo anterior, fue /www/
Creo
ls | sort -f | uniq -i -d
es más simple, más rápido y dará el mismo resultado
Tratar:
ls -1 | tr ''[A-Z]'' ''[a-z]'' | sort | uniq -c | grep -v " 1 "
Simple, realmente :-) ¿No son las tuberías maravillosas bestias?
El ls -1
le da los archivos uno por línea, el tr ''[AZ]'' ''[az]''
convierte todo en mayúscula a minúscula, el sort
ordena (sorprendentemente), uniq -c
elimina las ocurrencias subsecuentes de líneas duplicadas mientras da usted también cuenta y, finalmente, grep -v " 1 "
elimina aquellas líneas donde el conteo fue uno.
Cuando ejecuto esto en un directorio con un "duplicado" (copié qq
en qQ
), obtengo:
2 qq
Para la versión "este directorio y cada subdirectorio", simplemente reemplace 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 vuelve (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
Actualizar:
En realidad, en una mayor reflexión, el tr
minúscula todos los componentes de la ruta para que ambos
/a/b/c
/a/B/c
se considerarán duplicados aunque estén en directorios diferentes .
Si solo quiere duplicados dentro de un solo directorio para mostrar como una coincidencia, puede usar el (bastante monstruoso):
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úsculas de la porción final de la ruta en lugar de todo. Además, si solo desea archivos regulares (sin directorios, FIFOs, etc.), use find -type f
para restringir lo que se devuelve.
Esta es una pequeña y agradable aplicación de línea de comandos llamada findsn
que obtienes si compilas 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 se proporcionan argumentos, se busca en $ PATH cualquier archivo redundante o conflictivo.
-A reports all aliases (soft and hard links) to files.
If no path(s) specified then the $PATH is searched.
Si solo se especificaron las rutas, se verifican los archivos duplicados nombrados. 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. IE -c marcará los archivos y directorios que entrarán en conflicto si se transfieren a un sistema de archivos que no distingue entre mayúsculas y minúsculas. Observe si se especificó -c o -C y no se especificó ninguna ruta en la que se asume el directorio actual.
Siguiendo con la respuesta de mpez0, para detectar recursivamente simplemente reemplaza "ls" por "find". El único problema que veo con esto es que si este es un directorio que 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ás eliminando automáticamente estos archivos, ¿o sí?
find . | sort -f | uniq -i -d
Aquí hay un script que funcionó para mí (yo no soy el autor). el original y la discusión se pueden 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 funciona para usted, puede que tenga que cambiarlo. Por ejemplo
OLD : find "${1}" -type f | xargs -n 1 basename
NEW : find "${1}" -type f -printf "%f/n"
Un poco tarde para esto, pero aquí está la versión que fui con:
find . -type f | awk -F/ ''{print $NF}'' | sort -f | uniq -i -d
Aquí estamos usando:
-
find
- encuentra todos los archivos bajo el directorio actual -
awk
- elimina la parte de la ruta del archivo del nombre de archivo -
sort
- ordenar caso insensiblemente -
uniq
- encuentra a los engañados de lo que lo hace a través de la tubería
(Inspirado por la respuesta @ mpez0, y @SimonDowdles comenta en @paxdiablo answer.)
La otra respuesta es genial, pero en lugar de la secuencia de comandos perl "bastante monstruosa" sugiero
perl -pe ''s!([^/]+)$!lc $1!e''
Que minúscula solo el nombre del archivo parte 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{$_}++''
Edición 3: Encontré una solución usando sed, sort y uniq que también imprimirá los duplicados, pero solo 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
Edit 2: Y aquí hay un script más largo que imprimirá los nombres, toma una lista de rutas en stdin, como se da por find
. No 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";
}
}
Acabo de usar fdupes en CentOS para limpiar un montón de archivos duplicados ...
yum install fdupes
Aquí hay un ejemplo de cómo encontrar todos los archivos jar duplicados:
find . -type f -printf "%f/n" -name "*.jar" | sort -f | uniq -i -d
Reemplace *.jar
con el tipo de archivo duplicado que esté buscando.
Puede verificar duplicados en un directorio determinado con GNU awk:
gawk ''BEGINFILE {if ((seen[tolower(FILENAME)]++)) print FILENAME; nextfile}'' *
Esto usa BEGINFILE para realizar alguna acción antes de continuar y leer 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úsculas.
Si ya apareció un nombre, sin importar su caso, lo imprime. De lo contrario, simplemente salta al siguiente archivo.
Vea 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
Puedes usar:
find -type f -exec readlink -m {} /; | gawk ''BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}'' | uniq -c
Dónde:
find -type f
recursion print all full path.-exec readlink -m {} /;
obtener la ruta absoluta del archivogawk ''BEGIN{FS="/";OFS="/"}{$NF=tolower($NF);print}''
reemplace todos los nombres de archivo a minúsculasuniq -c
la ruta es única, -c muestra el recuento de duplicados.