Pourquoi le tri ls ignore-t-il les caractères non alphanumériques?

25

Lors du tri des noms de fichiers, lsignore les caractères comme -,_. Je m'attendais à ce qu'il utilise également ces caractères dans le tri.

Un exemple:

touch a1 a2 a-1 a-2 a_1 a_2 a.1 a.2 a,1 a,2

Affichez maintenant ces fichiers avec ls -1:

a1
a_1
a-1
a,1
a.1
a2
a_2
a-2
a,2
a.2

Je m'attendais à quelque chose comme ça:

a1
a2
a,1
a,2
a.1
a.2
a_1
a_2
a-1
a-2

c'est-à-dire que je m'attendais à ce que les caractères non alphanumériques soient pris en compte lors du tri.

Quelqu'un peut-il expliquer ce comportement? Ce comportement est-il imposé par une norme? Ou est-ce dû au fait que l'encodage est UTF-8?

Mise à jour: Il semble que cela soit lié au tri UTF-8:

$ LC_COLLATE=C ls -1
a,1
a,2
a-1
a-2
a.1
a.2
a1
a2
a_1
a_2
daniel kullmann
la source
2
UTF-8 et ASCII sont identiques si vous n'utilisez que les 128 premiers points de code (dont votre exemple est). Que se passe-t-il si vous le faites LC_COLLATE=C ls?
Alexios
Le problème n'est pas que ASCII et UTF-8 sont identiques, c'est plutôt que UTF-8 a ses propres règles de classement (tri).
daniel kullmann
1
Oui, c'est vrai qui [_-,.]sont regroupés et en quelque sorte semi-ignorés. Je ne sais pas exactement comment ou où une telle collecte est définie, mais il doit être un problème de classement, parce que tout simplement, et seulement, en changeant la collation C (via LC_COLLATE=C ls -l) est suffisant pour vous donner l'ordre de tri que vous attendiez ( en supposant que LC_ALLest pas prioritaire LC_COLLATE). Cela est vrai pour toute la gamme de caractères dans le plan multilingue de base Unicode ... J'ai modifié ma réponse pour inclure un exemple de script qui
confirme
si vous n'aimez pas comment cela fonctionne, vous pouvez créer un alias et le mettre dans votre ~ / .profile: alias ls = 'LC_COLLATE = C ls' </kbd>
jippie

Réponses:

10

Cela n'a rien à voir avec le jeu de caractères. C'est plutôt le langage qui détermine l'ordre de classement. La libc examine le langage présenté dans $LC_COLLATE/ $LC_ALL/ $LANGet recherche ses règles de classement (par exemple /usr/share/i18n/locales/*pour GLibC) et ordonne le texte comme indiqué.

Ignacio Vazquez-Abrams
la source
FYI: C'est plus compliqué que cela. Si l'on devait utiliser strcollpar exemple, vous verriez que quelque chose comme aasa.cserait trié ci-dessus aas.c.
Don Scott
12

EDIT: Test ajouté pour les données triées avec LC_COLLATE = C


La séquence d'assemblage par défaut traite ces caractères de "type ponctuation" comme étant de valeur égale .. Use LC_COLLATE=Cpour les traiter dans l'ordre des points de code ..

for i in 'a1' 'a_1' 'a-1' 'a,1' 'a.1' 'a2' 'a_2' 'a-2' 'a,2' 'a.2' ;do
  echo $i; 
done |LC_COLLATE=C sort

Sortie

a,1
a,2
a-1
a-2
a.1
a.2
a1
a2
a_1
a_2

Le code suivant teste tous les caractères UTF-8 valides dans le plan multilingue de base (sauf pour \ x00 et \ x0a ; pour plus de simplicité)
Il compare un fichier dans une séquence ascendante connue (générée), avec ce fichier trié aléatoirement puis trié à nouveau avec LC_COLLATE = C. Le résultat montre que la séquence C est identique à la séquence générée d'origine.

{ i=0 j=0 k=0 l=0
  for i in {0..9} {A..F} ;do
  for j in {0..9} {A..F} ;do
  for k in {0..9} {A..F} ;do
  for l in {0..9} {A..F} ;do
     (( 16#$i$j$k$l == 16#0000 )) && { printf '.' >&2; continue; }
     (( 16#$i$j$k$l == 16#000A )) && { printf '.' >&2; continue; }
     (( 16#$i$j$k$l >= 16#D800    && 
        16#$i$j$k$l <= 16#DFFF )) && { printf '.' >&2; continue; }
     (( 16#$i$j$k$l >= 16#FFFE )) && { printf '.' >&2; continue; }
     echo 0x"$i$j$k$l" |recode UTF-16BE/x4..UTF-8 || { echo "ERROR at codepoint $i$j$k$l " >&2; continue; } 
     echo 
  done
  done
  done; echo -n "$i$j$k$l " >&2
  done; echo >&2
} >listGen

             sort -R listGen    > listRandom
LC_COLLATE=C sort    listRandom > listCsort 

diff <(cat listGen;   echo "last line of listOrig " ) \
     <(cat listCsort; echo "last line of listCsort" )
echo 
cmp listGen listCsort; echo 'cmp $?='$?

Sortie:

63485c63485
< last line of listOrig 
---
> last line of listCsort

cmp $?=0
Peter.O
la source
2
Où est-ce documenté? Cela fait-il partie de la norme Unicode?
daniel kullmann
2
En fait, ils n'obtiennent pas la même valeur; ces caractères sont simplement ignorés lors du tri. S'ils étaient traités comme ayant une valeur égale, l'ordre de tri a_1 a2 a_2serait impossible.
daniel kullmann
+1 pour votre travail acharné et exemple de code. Après de nombreuses heures à trier les noms de répertoire avec des signes de ponctuation pour qu'ils correspondent à la façon dont treeje le pense, il y a plus dans l'histoire, comme la ponctuation supprimée des chaînes de comparaison ou quelque chose comme ça. Je peux dire que le /caractère doit être défini comme le caractère le plus bas de la séquence d'assemblage, quoi qu'il en soit.
WinEunuuchs2Unix