busca não sensível a maiúsculas e minúsculas de nomes de arquivos duplicados

16

Existe uma maneira de encontrar todos os arquivos em um diretório com nomes de arquivos duplicados, independentemente da caixa (letras maiúsculas e minúsculas)?

    
por lamcro 18.10.2011 / 21:02

5 respostas

13

Se você tiver utilitários GNU (ou pelo menos um conjunto que pode lidar com linhas com terminação zero) disponíveis, outra resposta tem um ótimo método:

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

Nota: a saída terá sequências com terminação zero; a ferramenta que você usa para processá-la ainda deve ser capaz de lidar com isso.

Na ausência de ferramentas que lidam com linhas com terminação zero, ou se você quer ter certeza de que seu código funciona em ambientes onde tais ferramentas não estão disponíveis, você precisa de um pequeno 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

O que é essa loucura? Consulte esta resposta para uma explicação das técnicas que tornam isso seguro para nomes de arquivos malucos.

    
por 18.10.2011 / 21:26
11

Existem muitas respostas complicadas acima, isso parece mais simples e rápido do que todas elas:

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

Se você quiser encontrar nomes de arquivos duplicados em subdiretórios, precisará comparar apenas o nome do arquivo, não o caminho completo:

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

Editar: Shawn J. Goff apontou que isso falhará se você tiver nomes de arquivos com caracteres de nova linha. Se você está usando utilitários GNU, você pode fazer estes funcionar também:

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

As opções -print0 (para localizar) e -z (para classificar e uniq) fazem com que trabalhem em sequências terminadas em NUL, em vez de sequências terminadas em nova linha. Como os nomes de arquivos não podem conter NUL, isso funciona para todos os nomes de arquivos.

    
por 26.10.2012 / 14:08
2

Classifique a lista de nomes de arquivos de uma maneira que não diferencia maiúsculas de minúsculas e imprima duplicatas. sort tem uma opção para classificação insensível a maiúsculas e minúsculas. O mesmo acontece com o GNU uniq , mas não com outras implementações, e tudo o que você pode fazer com uniq é imprimir todos os elementos em um conjunto de duplicatas, exceto o primeiro encontrado. Com as ferramentas GNU, supondo que nenhum nome de arquivo contenha uma nova linha, existe uma maneira fácil de imprimir todos os elementos, exceto um em cada conjunto de duplicados:

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

Portavelmente, para imprimir todos os elementos em cada conjunto de duplicatas, assumindo que nenhum nome de arquivo contenha uma nova linha:

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 }'

Se você precisar acomodar nomes de arquivos contendo novas linhas, vá para Perl ou Python. Observe que você pode precisar ajustar a saída ou, melhor, fazer o processamento posterior na mesma linguagem, já que o código de amostra abaixo usa novas linhas para separar nomes em sua própria saída.

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

Aqui está uma solução puramente zsh. É um pouco detalhado, já que não há uma maneira integrada de manter os elementos duplicados em um resultado de matriz ou glob.

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
    
por 19.10.2011 / 11:40
1

Sem GNU find :

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

    
por 19.10.2011 / 16:17
1

Eu finalmente consegui assim:

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

Eu usei find em vez de ls porque eu precisava do caminho completo (muitos subdiretórios) incluído. Não encontrei como fazer isso com ls .

    
por 19.10.2011 / 21:17