web-dev-qa-db-fra.com

Que fait "LC_ALL = C"?

Que signifie la valeur C pour LC_ALL faire dans les systèmes de type Unix?

Je sais que cela force les mêmes paramètres régionaux pour tous les aspects, mais que fait C?

344
jcubic

Il oblige les applications à utiliser la langue par défaut pour la sortie:

$ LC_ALL=es_ES man
¿Qué página de manual desea?

$ LC_ALL=C man
What manual page do you want?

et force le tri à être octet:

$ LC_ALL=en_US sort <<< $'a\nb\nA\nB'
a
A
b
B

$ LC_ALL=C sort <<< $'a\nb\nA\nB'
A
B
a
b
228

LC_ALL est la variable d'environnement qui remplace tous les autres paramètres de localisation ( sauf $LANGUAGE dans certaines circonstances ).

Différents aspects des localisations (comme le séparateur de milliers ou le caractère décimal, le jeu de caractères, l'ordre de tri, le mois, les noms de jour, la langue ou les messages d'application comme les messages d'erreur, le symbole monétaire) peuvent être définis à l'aide de quelques variables d'environnement.

Vous définissez généralement $LANG selon vos préférences avec une valeur qui identifie votre région (comme fr_CH.UTF-8 si vous êtes en Suisse romande, en utilisant UTF-8). Les variables LC_xxx individuelles remplacent un certain aspect. LC_ALL les remplace tous. La commande locale, lorsqu'elle est appelée sans argument, donne un résumé des paramètres actuels.

Par exemple, sur un système GNU, j'obtiens:

$ locale
LANG=en_GB.UTF-8
LANGUAGE=
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=

Je peux remplacer un paramètre individuel avec par exemple:

$ LC_TIME=fr_FR.UTF-8 date
jeudi 22 août 2013, 10:41:30 (UTC+0100)

Ou:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Ou remplacez tout avec LC_ALL.

$ LC_ALL=C LANG=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8 cat /
cat: /: Is a directory

Dans un script, si vous souhaitez forcer un paramètre spécifique, car vous ne savez pas quels paramètres l'utilisateur a forcés (éventuellement LC_ALL également), votre meilleure option, la plus sûre et généralement la seule, consiste à forcer LC_ALL.

Les paramètres régionaux C sont des paramètres régionaux spéciaux qui sont censés être les paramètres régionaux les plus simples. Vous pouvez également dire que si les autres paramètres régionaux sont réservés aux humains, les paramètres régionaux C concernent les ordinateurs. Dans les paramètres régionaux C, les caractères sont des octets simples, le jeu de caractères est ASCII (enfin, ce n'est pas obligatoire, mais en pratique, il sera dans les systèmes que la plupart d'entre nous utiliseront)), le l'ordre de tri est basé sur les valeurs d'octets, la langue est généralement l'anglais américain (bien que pour les messages d'application (par opposition aux choses comme les noms de mois ou de jour ou les messages des bibliothèques système), c'est à la discrétion de l'auteur de l'application) et des choses comme la devise les symboles ne sont pas définis.

Sur certains systèmes, il y a une différence avec les paramètres régionaux POSIX où, par exemple, l'ordre de tri des caractères non ASCII n'est pas défini.

Vous exécutez généralement une commande avec LC_ALL = C pour éviter que les paramètres de l'utilisateur n'interfèrent avec votre script. Par exemple, si vous voulez que [a-z] corresponde aux 26 ASCII de a à z, vous devez définir LC_ALL=C.

Sur les systèmes GNU, LC_ALL=C et LC_ALL=POSIX (ou LC_MESSAGES=C|POSIX) remplacent $LANGUAGE, tandis que LC_ALL=anything-else ne le ferait pas.

Quelques cas où vous devez généralement définir LC_ALL=C:

  • sort -u ou sort ... | uniq.... Dans de nombreux paramètres régionaux autres que C, sur certains systèmes (notamment GNU ceux), certains caractères ont le même ordre de tri . sort -u ne rapporte pas unique lignes, mais une de chaque groupe de lignes qui ont un ordre de tri égal. Donc, si vous voulez des lignes uniques, vous avez besoin d'une locale où les caractères sont en octets et tous les caractères ont un ordre de tri différent (ce que la locale C garantit ).
  • il en va de même pour l'opérateur = de l'opérateur POSIX conforme expr ou == l'opérateur de l'opérateur POSIX conforme awks (mawk et gawk ne sont pas POSIX à cet égard), qui ne vérifient pas si deux chaînes sont identiques mais si elles sont triées de la même manière.
  • Plages de caractères comme dans grep. Si vous voulez faire correspondre une lettre dans la langue de l'utilisateur, utilisez grep '[[:alpha:]]' et ne modifiez pas LC_ALL. Mais si vous souhaitez faire correspondre les a-zA-Z ASCII, vous avez besoin de LC_ALL=C grep '[[:alpha:]]' ou LC_ALL=C grep '[a-zA-Z]'¹. [a-z] correspond aux caractères qui sont triés après a et avant z (bien qu'avec de nombreuses API, c'est plus compliqué que cela). Dans d'autres paramètres régionaux, vous ne savez généralement pas ce que c'est. Par exemple, certains paramètres régionaux ignorent la casse pour le tri donc [a-z] dans certaines API comme les schémas bash, pourrait inclure [B-Z] ou [A-Y]. Dans de nombreux environnements locaux UTF-8 (y compris en_US.UTF-8 sur la plupart des systèmes) , [a-z] inclura les lettres latines de a à y avec des signes diacritiques mais pas celles de z (puisque z trie devant eux) que je ne peux pas imaginer serait ce que vous voulez (pourquoi voudriez-vous inclure é et non ź?).
  • arithmétique à virgule flottante dans ksh93. ksh93 respecte le paramètre decimal_point dans LC_NUMERIC. Si vous écrivez un script qui contient a=$((1.2/7)), il cessera de fonctionner lorsqu'il sera exécuté par un utilisateur dont les paramètres régionaux ont une virgule comme séparateur décimal:

    $ ksh93 -c 'echo $((1.1/2))'
    0.55
    $ LANG=fr_FR.UTF-8  ksh93 -c 'echo $((1.1/2))'
    ksh93: 1.1/2: arithmetic syntax error
    

    Ensuite, vous avez besoin de choses comme:

    #! /bin/ksh93 -
    float input="$1" # get it as input from the user in his locale
    float output
    arith() { typeset LC_ALL=C; (($@)); }
    arith output=input/1.2 # use the dot here as it will be interpreted
                           # under LC_ALL=C
    echo "$output" # output in the user's locale
    

    En remarque: le séparateur décimal , entre en conflit avec l'opérateur arithmétique , ce qui peut provoquer encore plus de confusion.

  • Lorsque vous avez besoin que les caractères soient des octets. De nos jours, la plupart des paramètres régionaux sont basés sur UTF-8, ce qui signifie que les caractères peuvent prendre de 1 à 6 octets. Lorsque vous traitez des données qui sont censées être des octets, avec des utilitaires de texte, vous voudrez définir LC_ALL = C. Cela améliorera également les performances de manière significative car l'analyse des données UTF-8 a un coût.
  • un corollaire du point précédent: lors du traitement de texte où vous ne savez pas dans quel jeu de caractères l'entrée est écrite, mais peut supposer qu'il est compatible avec ASCII (comme pratiquement tous les jeux de caractères le sont). Pour instance grep '<.*>' pour rechercher des lignes contenant une paire <, > ne fonctionnera pas si vous êtes dans un environnement local UTF-8 et que l'entrée est codée sur un seul octet 8 bits jeu de caractères comme iso8859-15. En effet, . ne correspond qu'aux caractères et les caractères non ASCII dans iso8859-15 ne risquent pas de former un caractère valide en UTF-8. D'autre part, LC_ALL=C grep '<.*>' fonctionne car toute valeur d'octet forme un caractère valide dans les paramètres régionaux C.
  • Chaque fois que vous traitez des données d'entrée ou des données de sortie qui ne sont pas destinées à/d'un humain. Si vous parlez à un utilisateur, vous voudrez peut-être utiliser sa convention et sa langue, mais par exemple, si vous générez des nombres pour alimenter une autre application qui attend des décimales de style anglais ou des noms de mois anglais, vous voudrez définir LC_ALL = C:

    $ printf '%g\n' 1e-2
    0,01
    $ LC_ALL=C printf '%g\n' 1e-2
    0.01
    $ date +%b
    août
    $ LC_ALL=C date +%b
    Aug
    

    Cela s'applique également à des choses comme la comparaison insensible à la casse (comme dans grep -i) et la conversion de casse (awk's toupper(), dd conv=ucase...). Par exemple:

    grep -i i
    

    n'est pas garanti de correspondre sur I dans les paramètres régionaux de l'utilisateur. Dans certains pays turcs, par exemple, ce n'est pas le cas en majuscule i est İ (notez le point) là et en minuscule I est ı (notez le point manquant).


¹ Selon l'encodage du texte, ce n'est pas nécessairement la bonne chose à faire. Cela est valable pour les jeux de caractères UTF-8 ou à un octet (comme l'iso-8859-1), mais pas nécessairement les jeux de caractères multi-octets non UTF-8.

Par exemple, si vous êtes dans un zh_HK.big5hkscs locale (Hong Kong, en utilisant la variante de Hong Kong du codage de caractères chinois BIG5), et que vous souhaitez rechercher des lettres anglaises dans un fichier codé dans ces jeux de caractères, en faisant soit :

LC_ALL=C grep '[[:alpha:]]'

ou

LC_ALL=C grep '[a-zA-Z]'

serait faux, car dans ce jeu de caractères (et bien d'autres, mais à peine utilisé depuis la sortie de l'UTF-8), beaucoup de caractères contiennent octets qui correspondent au codage ASCII des caractères A-Za-z. Par exemple, tous les A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽 (et bien d'autres) contiennent le codage de A. est 0x96 0x41, et A est 0x41 comme en ASCII. Donc notre LC_ALL=C grep '[a-zA-Z]' correspondrait sur les lignes qui contiennent ces caractères car il interpréterait mal ces séquences d'octets.

LC_COLLATE=C grep '[A-Za-z]'

fonctionnerait, mais uniquement si LC_ALL n'est pas défini autrement (ce qui remplacerait LC_COLLATE). Donc, vous devrez peut-être finir par faire:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

si vous souhaitez rechercher des lettres anglaises dans un fichier encodé dans l'encodage des paramètres régionaux.

359
Stéphane Chazelas

C est la locale par défaut, "POSIX" est l'alias de "C". Je suppose que "C" est dérivé de ANSI-C. Peut-être que ANSI-C définit les paramètres régionaux "POSIX".

8
Edward Shen

Pour autant que je sache, OS X utilise l'ordre de classement des points de code dans les environnements locaux UTF-8, c'est donc une exception à certains des points mentionnés dans la réponse de Stéphane Chazelas.

Cela imprime 26 sous OS X et 310 sous Ubuntu:

export LC_ALL=en_US.UTF-8
printf %b $(printf '\\U%08x\\n' $(seq $((0x11)) $((0x10ffff))))|grep -a '[a-z]'|wc -l

Le code ci-dessous n'imprime rien dans OS X, indiquant que l'entrée est triée. Les six caractères de substitution qui sont supprimés provoquent une erreur de séquence d'octets illégale.

export LC_ALL=en_US.UTF-8
for ((i=1;i<=0x1fffff;i++));do
  x=$(printf %04x $i)
  [[ $x = @(000a|d800|db7f|db80|dbff|dc00|dfff) ]]&&continue
  printf %b \\U$x\\n
done|sort -c

Le code ci-dessous n'imprime rien dans OS X, indiquant qu'il n'y a pas deux points de code consécutifs (au moins entre U + 000B et U + D7FF) qui ont le même ordre de classement.

export LC_ALL=en_US.UTF-8
for ((i=0xb;i<=0xd7fe;i++));do
  printf %b $(printf '\\U%08x\\n' $((i+1)) $i)|sort -c 2>/dev/null&&echo $i
done

(Les exemples ci-dessus utilisent %b parce que printf \\U25 entraîne une erreur dans zsh.)

Certains caractères et séquences de caractères qui ont le même ordre de classement dans GNU n'ont pas le même ordre de classement dans OS X. Cela imprime ① d'abord dans OS X (en utilisant _ sort ou GNU sort) mais ② d'abord dans Ubuntu:

export LC_ALL=en_US.UTF-8;printf %s\\n ② ①|sort

Cela affiche trois lignes dans OS X (en utilisant soit sort ou GNU sort) d'OS X mais une ligne dans Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
4
nisetama

Il semble que LC_COLLATE contrôle également "l'ordre alphabétique" utilisé par ls. Les paramètres régionaux américains seront triés comme suit:

a.C
aFilename.C
aFilename.H
a.H

en ignorant essentiellement les périodes. Vous préférerez peut-être:

a.C
a.H
aFilename.C
aFilename.H

Je fais certainement. Réglage LC_COLLATE à C accomplit cela. Notez qu'il triera également les minuscules après toutes les majuscules:

A.C
A.H
AFilename.C
a.C
a.H
4
SteveInCO