Respostas:
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.
-mindepth
's?
find
; Editei a resposta para incluir uma solução não-GNU.
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 -z
opçã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.
Classifique a lista de nomes de arquivos de maneira que não diferencie maiúsculas de minúsculas e imprima duplicatas. sort
tem 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
Sem GNU find
:
LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $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 tr
e clássico Unix tr
, operar em ÚNICA bytes e não são compatíveis Unicode ..
uniq
possui uma bandeira que não diferencia maiúsculas de minúsculas i.
Finalmente consegui desta maneira:
find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d
Eu usei, em find
vez da ls
causa, precisar do caminho completo (muitos subdiretórios) incluído. Não encontrei como fazer isso ls
.
sort
e uniq
possuem sinalizadores de ignorar maiúsculas e min respectivamente.
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