búsqueda entre mayúsculas y minúsculas de nombres de archivos duplicados

17

¿Hay alguna manera de encontrar todos los archivos en un directorio con nombres de archivo duplicados, independientemente de la carcasa (mayúsculas y / o minúsculas)?

lamcro
fuente

Respuestas:

14

Si tiene utilidades GNU (o al menos un conjunto que puede manejar líneas terminadas en cero) disponibles, otra respuesta tiene un excelente método:

find . -maxdepth 1 -print0 | sort -z | uniq -diz

Nota: la salida tendrá cadenas terminadas en cero; la herramienta que use para procesarlo más adelante debería poder manejar eso.

En ausencia de herramientas que se ocupen de líneas terminadas en cero, o si desea asegurarse de que su código funcione en entornos donde tales herramientas no están disponibles, necesita un pequeño script:

#!/bin/sh
for f in *; do
  find . -maxdepth 1 -iname ./"$f" -exec echo \; | wc -l | while read count; do
    [ $count -gt 1 ] && echo $f
  done
done

¿Qué es esta locura? Consulte esta respuesta para obtener una explicación de las técnicas que hacen que esto sea seguro para los nombres de archivos locos.

Shawn J. Goff
fuente
1
Solo iba a publicar un mensaje similar ... Pero peor respuesta :)
rozcietrzewiacz
2
¿Realmente necesitas los -mindepth's?
rozcietrzewiacz
Estoy usando Solaris. ¿Es / usr / bin / encuentra el que está hablando? Intenté usarlo y me dio muchos errores.
lamcro
@lamcro No, Solaris no usa GNU's find; He editado la respuesta para incluir una solución que no sea GNU.
Shawn J. Goff
Okay. ¿Lo pego en un archivo de texto y le doy derechos de ejecución?
lamcro
12

Hay muchas respuestas complicadas arriba, esto parece más simple y rápido que todas:

find . -maxdepth 1 | sort -f | uniq -di

Si desea encontrar nombres de archivos duplicados en subdirectorios, debe comparar solo el nombre del archivo, no la ruta completa:

find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di

Editar: Shawn J. Goff ha señalado que esto fallará si tiene nombres de archivo con caracteres de nueva línea. Si está utilizando las utilidades de GNU, también puede hacer que funcionen:

find . -maxdepth 1 -print0 | sort -fz | uniq -diz

La opción -print0(para buscar) y -z(para ordenar y uniq) hacen que funcionen en cadenas terminadas en NUL, en lugar de cadenas terminadas en nueva línea. Como los nombres de archivo no pueden contener NUL, esto funciona para todos los nombres de archivo.

Jamie Kitson
fuente
1
Pero vea mi comentario sobre la respuesta de Shawn J. Goff, puede agregar la opción -print0 para buscar y la opción -z para unificar y ordenar. Además, también quieres -f en orden. Entonces funciona. (Voy a editar esto en su respuesta, siéntase libre de revertirlo si no lo aprueba)
derobert
El último comando me está dando salida sin retornos de carro (el resultado está todo en una línea). Estoy usando Red Hat Linux para ejecutar el comando. La primera línea de comando funciona mejor para mí.
Dom
2

Ordene la lista de nombres de archivo sin distinción entre mayúsculas y minúsculas e imprima duplicados. sorttiene una opción para ordenar sin distinción entre mayúsculas y minúsculas. También lo hace GNU uniq, pero no otras implementaciones, y todo lo que puede hacer uniqes imprimir cada elemento en un conjunto de duplicados, excepto el primero que se encuentre. Con las herramientas GNU, suponiendo que ningún nombre de archivo contenga una nueva línea, hay una manera fácil de imprimir todos los elementos, excepto uno en cada conjunto de duplicados:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id

Portablemente, para imprimir todos los elementos en cada conjunto de duplicados, suponiendo que ningún nombre de archivo contenga una nueva línea:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
awk '
    tolower($0) == tolower(prev) {
        print prev;
        while (tolower($0) == tolower(prev)) {print; getline}
    }
    1 { prev = $0 }'

Si necesita acomodar nombres de archivo que contengan nuevas líneas, vaya a Perl o Python. Tenga en cuenta que es posible que necesite modificar la salida, o mejor hacer su procesamiento adicional en el mismo idioma, ya que el código de muestra a continuación usa nuevas líneas para separar los nombres en su propia salida.

perl -e '
    foreach (glob("*")) {push @{$f{lc($_)}}, $_}
    foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'

Aquí hay una solución zsh pura. Es un poco detallado, ya que no hay una forma integrada de mantener los elementos duplicados en una matriz o resultado global.

a=(*)(N); a=("${(@io)a}")
[[ $#a -le 1 ]] ||
for i in {2..$#a}; do
  if [[ ${(L)a[$i]} == ${(L)a[$((i-1))]} ]]; then
    [[ ${(L)a[$i-2]} == ${(L)a[$((i-1))]} ]] || print -r $a[$((i-1))]
    print -r $a[$i]
  fi
done
Gilles 'SO- deja de ser malvado'
fuente
1

Sin GNU find:

LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'

Rudolf Adamkovic
fuente
2
tres muy probable que cause estragos en cualquier conjunto de caracteres que use más de un byte por carácter. Solo los primeros 256 caracteres de UTF-8 son seguros cuando se usan tr. De Wikipedia tr (Unix) . La mayoría de las versiones tr, incluyendo GNU try Unix clásico tr, operan en SINGLE BYTES y no son compatibles con Unicode ..
Peter.O
1
Actualice mi comentario anterior ... solo los primeros 128 caracteres de UTF-8 son seguros. Todos los caracteres UTF-8 por encima del rango ordinal 0..127 son todos de varios bytes y pueden tener valores de bytes individuales en otros caracteres. Solo los bytes en el rango 0..127 tienen una asociación uno a uno con un carácter único.
Peter.O
Plus uniqtiene una bandera que no distingue entre mayúsculas y minúsculas i.
Jamie Kitson
1

Finalmente lo logré de esta manera:

find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d

Utilicé en findlugar de lsporque necesitaba la ruta completa (muchos subdirectorios) incluida. No encontré cómo hacer esto ls.

lamcro
fuente
2
Ambos sorty uniqtienen banderas de ignorar mayúsculas y minúsculas, f e i respectivamente.
Jamie Kitson
-1

Para cualquier otra persona que quiera cambiar el nombre, etc., uno de los archivos:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
usuario3342930
fuente