Dans un système de fichiers où les noms de fichiers sont en UTF-8, j'ai un fichier avec un nom erroné; il est affiché comme D�sinstaller
:, nom réel selon zsh D$'\351'sinstaller
:, Latin1 pour Désinstaller
, lui-même une barbarie française pour "désinstaller". Zsh ne le ferait pas correspondre [[ $file =~ '^.*$' ]]
mais le ferait avec un globbing *
- c'est le comportement que j'attends.
Maintenant, je m'attends toujours à le trouver lors de l'exécution find . -name '*'
- en fait, je ne m'attendrais jamais à ce qu'un nom de fichier échoue à ce test. Cependant, avec LANG=en_US.utf8
, le fichier ne pas apparaître, et je dois ensemble LANG=C
(ou en_US
, ou ''
) pour que cela fonctionne.
Question: Quelle est la mise en œuvre derrière et comment aurais-je pu prédire ce résultat?
Infos: Arch Linux 3.14.37-1-lts, find (GNU findutils) 4.4.2
convmv
de convertir les noms de fichiers en utf-8?[[ $file =~ '^.*$' ]]
ne pas utiliserrecode
le nom de fichier, mais je vais maintenant vérifierconvmv
si besoin est. Merci.Réponses:
C'est vraiment une belle prise. D'un rapide coup d'œil au code source de GNU find, je dirais que cela se résume à la façon dont
fnmatch
se comporte les séquences d'octets invalides (pred_name_common
enpred.c
):Ce code teste la valeur de retour de
fnmatch
pour l'égalité avec 0, mais ne vérifie pas les erreurs; cela se traduit par des erreurs signalées comme "ne correspond pas".Il a été suggéré, il y a de nombreuses années, de changer le comportement de cette fonction libc pour toujours retourner vrai sur le
*
modèle, même sur les noms de fichiers cassés, mais d'après ce que je peux dire, l'idée doit avoir été rejetée (voir le fil commençant à https : //sourceware.org/ml/libc-hacker/2002-11/msg00071.html ):Comme mentionné par Stéphane Chazelas dans un commentaire, et également dans le même fil de 2002, cela est incompatible avec l'expansion globale effectuée par les shells, qui ne s'étouffent pas avec les caractères invalides. Peut-être encore plus déroutant est le fait que l'inversion du test ne correspondra qu'aux fichiers qui ont des noms cassés (créez des fichiers en bash avec
touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'
):Donc, pour répondre à votre question, vous auriez pu prédire cela en connaissant le comportement de votre
fnmatch
dans ce cas, et en sachant commentfind
gère la valeur de retour de cette fonction; vous n'auriez probablement pas pu le découvrir uniquement en lisant la documentation.la source
*
c'est que ce serait incompatible avecD*staller
.D*staller
correspondre$'D\351sinstaller'
aussi bien que dans le glob de tous les shells que j'ai testés. Étant donné que le comportement GNU fnmatch n'est pas cohérent avec celui du shell GNU, je dirais que c'est un bug..
ne doit correspondre qu'à des caractères valides dans l'encodage - d'où mon attente qui.*
ne correspond pas à des chaînes non valides - mais je ne trouve pas de spécification correspondante pour l'étoile globulante.-name '*'
correspond à tous les fichiers, noms brisés inclus), donc vraisemblablement la version de BSD defnmatch
, qui ne revendique pas la cnoformance POSIX.2, contrairement à la version GNU, a une interprétation différente, et sans doute plus saine, de ce qui devrait être fait sur les caractères invalides.L'
-name
option find utilise la notation de correspondance de modèle de shell pour effectuer la correspondance du nom de fichier.*
est un modèle correspondant à plusieurs caractères , doit correspondre à une chaîne de zéro ou plusieurs caractères.find
utilise fnmatch pour vérifier la correspondance des modèles, vous pouvez donc utiliser ltrace pour vérifier le résultat:Avec
D\351sinstaller
,fnmatch
retour-1
, a indiqué qu'il ne correspondait pas. Un caractère valide commeሒaa
sera mis en correspondance.Dans votre cas, avec
UTF-8
locale,\351
est un caractère non valide, provoquant l'échec de la correspondance de modèle.la source
ltrace
. Je le savaisstrace
, maisltrace
c'est nouveau pour moi. Charmant!