identifier les fichiers avec des caractères non ASCII ou non imprimables dans le nom de fichier

24

Dans un répertoire de 80 Go contenant environ 700 000 fichiers, il y a des noms de fichiers avec des caractères non anglais dans le nom de fichier. En plus de parcourir la liste des fichiers laborieusement, il existe:

  • Un moyen facile de répertorier ou d'identifier autrement ces noms de fichiers?
  • Un moyen de générer des caractères imprimables non anglais - ces caractères qui ne sont pas répertoriés dans la plage imprimable de man ascii(afin que je puisse tester que ces fichiers sont en cours d'identification)?
suspect
la source

Réponses:

32

En supposant que "étranger" signifie "pas un caractère ASCII", vous pouvez utiliser findavec un modèle pour rechercher tous les fichiers ne contenant pas de caractères ASCII imprimables dans leurs noms:

LC_ALL=C find . -name '*[! -~]*'

(L'espace est le premier caractère imprimable répertorié sur http://www.asciitable.com/ , ~est le dernier.)

L'astuce pour LC_ALL=Cest requise (en fait, LC_CTYPE=Cet LC_COLLATE=C), sinon la plage de caractères n'est pas interprétée correctement. Voir aussi la page de manuel glob(7). Étant donné que les LC_ALL=Ccauses findinterprètent les chaînes comme ASCII, il imprime des caractères multi-octets (tels que π) comme des points d'interrogation. Pour résoudre ce problème, catredirigez vers un programme (par exemple ) ou redirigez vers un fichier.

Au lieu de spécifier des plages de caractères, [:print:]peut également être utilisé pour sélectionner des "caractères imprimables". Assurez-vous de définir les paramètres régionaux C ou vous obtenez un comportement tout à fait (apparemment) arbitraire.

Exemple:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π
Lekensteyn
la source
1
N'oubliez pas que certains noms de fichiers utilisent des jeux de caractères étrangers incompatibles avec UTF-8 ou ASCII. Dans ces cas, vous pouvez voir des points d'interrogation au lieu de caractères.
Lekensteyn
1
+1, mais j'utiliserais à la LC_ALL=Cplace de LC_COLLATE=Ccar cela n'a pas beaucoup de sens de définir LC_COLLATE sur C sans définir LC_CTYPEet de s'assurer qu'il fonctionne toujours même lorsque la variable LC_ALL est dans l'environnement.
Stéphane Chazelas
Si SPCest imprimable , qu'en est- TABil de ceux LFqui se trouvent généralement dans les fichiers texte?
Stéphane Chazelas
1
Merci - cela a trouvé six fichiers, qui avaient un trait d'union long, un trait d'union court et une variante de guillemet simple. Ceux-ci provenaient tous de MS Word. Aucune différence dans les fichiers répertoriés entre LC_ALL et LC_COLLATE. LC_COLLATE affiche correctement les caractères non ASCII alors que LC_ALL affiche ??? au lieu. Excellente réponse!
suspectus
1
@suspectus J'ai mis à jour par réponse sur la base des suggestions de Stéphane. Pour LC_COLLATEet LC_CTYPE, voir aussi la find(1)page de manuel.
Lekensteyn
6

Si vous traduisez chaque nom de fichier à l'aide tr -d '[\200-\377]'et le comparez avec le nom d'origine, tous les noms de fichier comportant des caractères spéciaux ne seront pas les mêmes.

(Ce qui précède en supposant que vous voulez dire non ASCII avec étranger)

Timo
la source
2
Cela supprime également [et ]dans la plupart des trimplémentations.
Stéphane Chazelas
Oui - il a supprimé [et ]sur mon système.
suspectus
+1 - la solution a trouvé tous les (six) noms de fichiers avec des symboles non ASCII (en plus du [et des ]s). Merci.
suspectus
3

Vous pouvez utiliser trpour supprimer tout caractère étranger d'un nom de fichier et comparer le résultat avec le nom de fichier d'origine pour voir s'il contenait des caractères étrangers.

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames
Ernest A
la source
4
c'est une belle extension à ma réponse, mais c'est trop simple, les noms de fichiers peuvent avoir des retours à la ligne et votre script ne fonctionnera pas
Timo
1
Si vous souhaitez post-traiter la findsortie, utilisez la sortie / entrée terminée par NUL comme indiqué dans cette réponse .
Lekensteyn
0

La réponse acceptée est utile, mais si vos noms de fichiers sont déjà dans l'encodage spécifié dans LANG/ LC_CTYPE, il est préférable de simplement faire:

LC_COLLATE=C find . -name '*[! -~]*'

Les classes de caractères sont affectées par LC_CTYPE, mais la commande ci-dessus n'utilise pas de classes de caractères, uniquement des plages, doncLC_CTYPE empêche simplement les caractères inhabituels d'être remplacés par des points d'interrogation.

SamB
la source