Réponses:
Si vous avez des utilitaires GNU (ou au moins un ensemble qui peut traiter les lignes terminées par zéro) disponibles, une autre réponse a une excellente méthode:
find . -maxdepth 1 -print0 | sort -z | uniq -diz
Remarque: la sortie aura des chaînes terminées par zéro; l'outil que vous utilisez pour poursuivre le traitement devrait pouvoir gérer cela.
En l'absence d'outils traitant des lignes terminées par zéro, ou si vous voulez vous assurer que votre code fonctionne dans des environnements où ces outils ne sont pas disponibles, vous avez besoin d'un petit 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
Quelle est cette folie? Voir cette réponse pour une explication des techniques qui rendent cela sûr pour les noms de fichiers fous.
-mindepth
?
find
; J'ai édité la réponse pour inclure une solution non GNU.
Il y a beaucoup de réponses compliquées ci-dessus, cela semble plus simple et plus rapide que toutes:
find . -maxdepth 1 | sort -f | uniq -di
Si vous souhaitez trouver des noms de fichiers en double dans les sous-répertoires, vous devez comparer uniquement le nom de fichier, pas le chemin complet:
find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di
Edit: Shawn J. Goff a souligné que cela échouera si vous avez des noms de fichiers avec des caractères de nouvelle ligne. Si vous utilisez des utilitaires GNU, vous pouvez également les faire fonctionner:
find . -maxdepth 1 -print0 | sort -fz | uniq -diz
L' option -print0
(pour la recherche) et l' -z
option (pour le tri et l'uniq) les obligent à travailler sur des chaînes terminées par NUL, au lieu de chaînes terminées par une nouvelle ligne. Étant donné que les noms de fichiers ne peuvent pas contenir NUL, cela fonctionne pour tous les noms de fichiers.
Triez la liste des noms de fichiers de manière non sensible à la casse et imprimez les doublons. sort
a une option pour le tri non sensible à la casse. Il en va de même pour GNU uniq
, mais pas pour les autres implémentations, et tout ce que vous pouvez faire uniq
est d'imprimer chaque élément d'un ensemble de doublons, sauf le premier rencontré. Avec les outils GNU, en supposant qu'aucun nom de fichier ne contient de nouvelle ligne, il existe un moyen facile d'imprimer tous les éléments sauf un dans chaque jeu de doublons:
for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id
Portablement, pour imprimer tous les éléments dans chaque jeu de doublons, en supposant qu'aucun nom de fichier ne contient de nouvelle ligne:
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 vous devez accepter des noms de fichiers contenant des sauts de ligne, optez pour Perl ou Python. Notez que vous devrez peut-être modifier la sortie, ou mieux effectuer votre traitement ultérieur dans la même langue, car l'exemple de code ci-dessous utilise des retours à la ligne pour séparer les noms dans sa propre sortie.
perl -e '
foreach (glob("*")) {push @{$f{lc($_)}}, $_}
foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'
Voici une pure solution zsh. C'est un peu verbeux, car il n'y a pas de moyen intégré de conserver les éléments en double dans un tableau ou un résultat 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
Sans GNU find
:
LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'
tr
est très susceptible de faire des ravages sur tout jeu de caractères qui utilise plus d'un octet par caractère. Seuls les 256 premiers caractères de l'UTF-8 sont sûrs lors de l'utilisation tr
. De Wikipedia tr (Unix) .. La plupart des versions de tr
, y compris GNU tr
et Unix classique tr
, fonctionnent sur SINGLE BYTES et ne sont pas conformes à Unicode ..
uniq
a un indicateur insensible à la casse i.
J'ai finalement réussi comme ça:
find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d
J'ai utilisé à la find
place de la ls
cause, j'avais besoin du chemin complet (beaucoup de sous-répertoires) inclus. Je n'ai pas trouvé comment faire ça avec ls
.
sort
et uniq
ont respectivement des indicateurs ignorer la casse, f et i.
Pour toute autre personne qui souhaite ensuite renommer etc. l'un des fichiers:
find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done