recherche insensible à la casse des noms de fichiers en double


17

Existe-t-il un moyen de trouver tous les fichiers dans un répertoire avec des noms de fichiers en double, indépendamment de la casse (majuscules et / ou minuscules)?

Réponses:


14

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.


1
J'allais juste poster un similaire ... Mais pire réponse :)
rozcietrzewiacz

2
Avez-vous vraiment besoin du -mindepth?
rozcietrzewiacz

J'utilise Solaris. Est-ce que / usr / bin / trouve celui dont vous parlez? J'ai essayé de l'utiliser et m'a donné de nombreuses erreurs.
lamcro

@lamcro Non, Solaris n'utilise pas de GNU find; J'ai édité la réponse pour inclure une solution non GNU.
Shawn J.Goff

D'accord. Dois-je simplement le coller dans un fichier texte et lui donner des droits d'exécution?
lamcro

12

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' -zoption (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.


1
Mais voyez mon commentaire sur la réponse de Shawn J. Goff, vous pouvez ajouter l'option -print0 pour trouver, et l'option -z à uniq et trier. En outre, vous souhaitez également -f sur le tri. Alors ça marche. (Je vais modifier cela dans votre réponse, n'hésitez pas à revenir si vous n'approuvez pas)
derobert

La dernière commande me donne une sortie sans retour chariot (le résultat est tout sur une seule ligne). J'utilise Red Hat Linux pour exécuter la commande. La première ligne de commande fonctionne le mieux pour moi.
dim

2

Triez la liste des noms de fichiers de manière non sensible à la casse et imprimez les doublons. sorta 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 uniqest 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

1

Sans GNU find:

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


2
trest 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 tret Unix classique tr, fonctionnent sur SINGLE BYTES et ne sont pas conformes à Unicode ..
Peter.O

1
Mise à jour de mon commentaire précédent .. seuls les 128 premiers caractères de l'UTF-8 sont sûrs. Tous les caractères UTF-8 au-dessus de la plage ordinale 0..127 sont tous multi-octets et peuvent avoir des valeurs d'octets individuelles dans d'autres caractères. Seuls les octets compris entre 0 et 127 ont une association un à un avec un caractère unique.
Peter.O

De plus uniqa un indicateur insensible à la casse i.
Jamie Kitson

1

J'ai finalement réussi comme ça:

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

J'ai utilisé à la findplace de la lscause, j'avais besoin du chemin complet (beaucoup de sous-répertoires) inclus. Je n'ai pas trouvé comment faire ça avec ls.


2
Les deux sortet uniqont respectivement des indicateurs ignorer la casse, f et i.
Jamie Kitson

-1

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.