pesquisa sem distinção entre maiúsculas e minúsculas de nomes de arquivos duplicados

17

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

lamcro
fonte

Respostas:

14

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

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

Nota: a saída terá cadeias terminadas em zero; a ferramenta que você usa para continuar processando deve poder lidar com isso.

Na ausência de ferramentas que lidam com linhas com terminação zero ou se você deseja garantir que seu código funcione em ambientes onde essas 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

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

Shawn J. Goff
fonte
1
Eu estava indo para postar uma resposta semelhante ... Mas o pior :)
rozcietrzewiacz
2
Você realmente precisa dos -mindepth's?
rozcietrzewiacz 18/10/11
Estou usando o Solaris. O / usr / bin / encontra o que você está falando? Eu tentei usá-lo e me deu muitos erros.
lamcro
@lamcro Não, o Solaris não usa GNU find; Editei a resposta para incluir uma solução não-GNU.
Shawn J. Goff
Está bem. Eu apenas colo em um arquivo de texto e concedo direitos de execução?
lamcro
12

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

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

Se você deseja encontrar nomes de arquivos duplicados nos subdiretórios, precisa comparar apenas o nome do arquivo, não o caminho inteiro:

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

Edit: Shawn J. Goff apontou que isso irá falhar se você tiver nomes de arquivos com caracteres de nova linha. Se você estiver usando utilitários GNU, também poderá fazer o seguinte:

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

A opção -print0(para localização) e a -zopção (para classificação e uniq) fazem com que funcionem em cadeias terminadas em NUL, em vez de cadeias terminadas em nova linha. Como os nomes dos arquivos não podem conter NUL, isso funciona para todos os nomes de arquivos.

Jamie Kitson
fonte
1
Mas veja meu comentário sobre a resposta de Shawn J. Goff, você pode adicionar a opção -print0 para encontrar e a opção -z para uniq e classificar. Além disso, você deseja -f na classificação também. Então funciona. (Vou editar isso em sua resposta, sinta-se livre para reverter, se você não aprovar)
derobert
O último comando está me dando uma saída sem retorno de carro (o resultado é tudo em uma linha). Estou usando o Red Hat Linux para executar o comando. A primeira linha de comando funciona melhor para mim.
Sun
2

Classifique a lista de nomes de arquivos de maneira que não diferencie maiúsculas de minúsculas e imprima duplicatas. sorttem uma opção para classificação sem distinção entre maiúsculas e minúsculas. O mesmo acontece com o GNU uniq, mas não em outras implementações, e tudo o que você pode fazer uniqé imprimir todos os elementos em um conjunto de duplicatas, exceto a primeira encontrada. Com as ferramentas GNU, assumindo 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 duplicatas:

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

Portably, 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, processar seu processamento no mesmo idioma, pois o código de exemplo 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 zsh pura. É um pouco detalhado, pois não há uma maneira integrada de manter os elementos duplicados em um resultado de matriz ou 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- parar de ser mau'
fonte
1

Sem GNU find:

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

Rudolf Adamkovic
fonte
2
tré muito provável que cause estragos em qualquer conjunto de caracteres que use mais de um único byte por caractere. Somente os primeiros 256 caracteres do UTF-8 são seguros quando usados tr. De Wikipedia tr (Unix) .. A maioria das versões tr, incluindo GNU tre clássico Unix tr, operar em ÚNICA bytes e não são compatíveis Unicode ..
Peter.O
1
Atualize para o meu comentário anterior. Apenas os primeiros 128 caracteres do UTF-8 são seguros. Todos os caracteres UTF-8 acima do intervalo ordinal 0..127 são todos de vários bytes e podem ter valores de bytes individuais em outros caracteres. Somente os bytes no intervalo 0..127 têm uma associação individual para um caractere exclusivo.
precisa saber é o seguinte
O Plus uniqpossui uma bandeira que não diferencia maiúsculas de minúsculas i.
Jamie Kitson
1

Finalmente consegui desta maneira:

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

Eu usei, em findvez da lscausa, precisar do caminho completo (muitos subdiretórios) incluído. Não encontrei como fazer isso ls.

lamcro
fonte
2
Ambos sorte uniqpossuem sinalizadores de ignorar maiúsculas e min respectivamente.
Jamie Kitson
-1

Para quem quiser renomear etc, um dos arquivos:

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