O que faz "LC_ALL = C"?

324

Qual é o Cvalor para LC_ALLsistemas Unix?

Eu sei que ele força o mesmo local para todos os aspectos, mas o que Cfaz?

jcubic
fonte
Se você quiser resolver um problema com o xclockaviso ( Missing charsets in String to FontSet conversion), será melhor usar isso LC_ALL=C.UTF-8para evitar problemas com o cirílico. Para definir essa variável de ambiente, você deve adicionar a seguinte linha ao final do ~/.bashrcarquivo -export LC_ALL=C.UTF-8
fedotsoldier
@fedotsoldier você provavelmente deve fazer uma pergunta e dar a resposta você mesmo, não acho que esteja relacionado à pergunta. É apenas a resposta para um problema diferente que você está tendo.
jcubic 19/06
Sim, você está certo, ok
fedotsoldier 19/06

Respostas:

209

Força os aplicativos a usar o idioma padrão para saída:

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

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

e força a classificação a bytes:

$ 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
Ignacio Vazquez-Abrams
fonte
20
+1 para bons exemples, mas falta a informação importante que estão em resposta de Stephane ...
Olivier Dulac
4
O que você quer dizer com idioma padrão ?
Stéphane Chazelas
2
Sim, eu entendo que o autor pode fazer o que quiser, incluindo não fazer o que diz na lata. A coisa é. O inglês dos EUA é o único idioma que pode ser representado corretamente com o conjunto de caracteres em LC_ALL = C, o único idioma em que a ordem de classificação em LC_ALL = C (LC_COLLATE) faz sentido, LC_ALL = C (LC_TIME) tem nomes em inglês para mês e dia. Eu nunca vi aplicativos em que LC_ALL = C retornou uma mensagem em um idioma diferente de LC_ALL = en LANGUAGE = en. Então, tenho o direito de relatar um bug em um programa, se esse não for o caso? (sem falar de aplicativos não traduzidos para o inglês aqui).
Stéphane Chazelas
2
O problema é "inglês dos EUA é o único idioma que pode ser representado corretamente com o conjunto de caracteres em LC_ALL = C". Isso geralmente é verdadeiro apenas em programas C / C ++ ao usar caracteres estreitos, mas mesmo assim há exceções (já que existem vários idiomas que usam apenas caracteres e símbolos encontrados em ASCII). Relatar um erro quando o idioma padrão não é o inglês fará você parecer ... fanático.
Ignacio Vazquez-Abrams
3
Observe que em inglês (que significa LANG = en_US.utf8) as mensagens podem (e devem) usar caracteres unicode como "" para citar cadeias de caracteres. Enquanto em LANG = C, ele possui apenas ASCII (aspas duplas, aspas e apóstrofos).
Ángel
332

LC_ALLé a variável de ambiente que substitui todas as outras configurações de localização ( exceto $LANGUAGEem algumas circunstâncias ).

Diferentes aspectos das localizações (como o separador de mil caracteres ou o ponto decimal, o conjunto de caracteres, a ordem de classificação, o mês, os nomes dos dias, o idioma ou as mensagens do aplicativo, como mensagens de erro, símbolo da moeda) podem ser definidos usando algumas variáveis ​​de ambiente.

Você normalmente definirá $LANGsua preferência com um valor que identifique sua região (como fr_CH.UTF-8se você estiver na Suíça de língua francesa, usando UTF-8). As LC_xxxvariáveis individuais substituem um determinado aspecto. LC_ALLsubstitui todos eles. O localecomando, quando chamado sem argumento, fornece um resumo das configurações atuais.

Por exemplo, em um sistema GNU, recebo:

$ 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=

Posso substituir uma configuração individual por, por exemplo:

$ 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 substitua tudo por LC_ALL.

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

Em um script, se você deseja forçar uma configuração específica, como não sabe quais configurações o usuário forçou (possivelmente LC_ALL também), sua melhor opção, mais segura e geralmente única é forçar LC_ALL.

O Ccódigo de idioma é um código de idioma especial que deve ser o código de idioma mais simples. Você também pode dizer que, enquanto os outros locais são para humanos, o local C é para computadores. No código C, os caracteres são bytes únicos, o conjunto de caracteres é ASCII (bem, não é necessário, mas na prática estará nos sistemas que a maioria de nós utilizará), a ordem de classificação é baseada nos valores de bytes, o idioma geralmente é inglês dos EUA (embora para mensagens do aplicativo (em oposição a itens como nomes de mês ou dia ou mensagens das bibliotecas do sistema), fique a critério do autor do aplicativo) e itens como símbolos de moeda não estejam definidos.

Em alguns sistemas, há uma diferença com o código do idioma POSIX em que, por exemplo, a ordem de classificação para caracteres não ASCII não está definida.

Você geralmente executa um comando com LC_ALL = C para evitar que as configurações do usuário interfiram no seu script. Por exemplo, se você deseja [a-z]combinar os 26 caracteres ASCII de apara z, você deve definir LC_ALL=C.

Nos sistemas GNU, LC_ALL=Ce LC_ALL=POSIX(ou LC_MESSAGES=C|POSIX) substituem $LANGUAGE, enquanto LC_ALL=anything-elsenão.

Alguns casos em que você normalmente precisa definir LC_ALL=C:

  • sort -uou sort ... | uniq.... Em muitos locais diferentes de C, em alguns sistemas (principalmente os GNU), alguns caracteres têm a mesma ordem de classificação . sort -unão relata linhas exclusivas, mas uma de cada grupo de linhas que têm a mesma ordem de classificação. Portanto, se você quiser linhas exclusivas, precisará de um código de idioma em que os caracteres sejam byte e todos os caracteres terão uma ordem de classificação diferente (que o Ccódigo de idioma garante).
  • o mesmo se aplica ao =operador compatível com POSIX exprou ao ==operador compatível com POSIX awk( mawke gawknão é POSIX a esse respeito), que não verifica se duas cadeias são idênticas, mas se elas são iguais.
  • Intervalos de caracteres como em grep. Se você deseja corresponder uma letra no idioma do usuário, use grep '[[:alpha:]]'e não modifique LC_ALL. Mas se você quiser combinar os a-zA-Zcaracteres ASCII, precisará de um LC_ALL=C grep '[[:alpha:]]'ou de LC_ALL=C grep '[a-zA-Z]'¹. [a-z]corresponde aos caracteres que ordenam aantes e depois z(embora com muitas APIs seja mais complicado que isso). Em outros locais, você geralmente não sabe o que são. Por exemplo, alguns códigos de idioma ignoram os casos de classificação, portanto, [a-z]em algumas APIs, como bashpadrões, podem incluir [B-Z]or [A-Y]. Em muitos locais UTF-8 (incluindo en_US.UTF-8na maioria dos sistemas), [a-z]incluirá as letras latinas de apara ycom diacríticos, mas não as de z(desdeztipos antes deles) que eu não consigo imaginar seria o que você quer (por que você gostaria de incluir ée não ź?).
  • ponto aritmético de ponto flutuante ksh93. ksh93homenageia o decimal_pointcenário LC_NUMERIC. Se você escrever um script que contenha a=$((1.2/7)), ele deixará de funcionar quando executado por um usuário cuja localidade possui vírgula como separador decimal:

    $ 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
    

    Então você precisa de coisas como:

    #! /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
    

    Como observação lateral: o ,separador decimal entra em conflito com o ,operador aritmético, o que pode causar ainda mais confusão.

  • Quando você precisa que os caracteres sejam bytes. Atualmente, a maioria das localidades é baseada em UTF-8, o que significa que os caracteres podem ocupar de 1 a 6 bytes. Ao lidar com dados que devem ser bytes, com utilitários de texto, convém definir LC_ALL = C. Também melhorará significativamente o desempenho porque a análise de dados UTF-8 tem um custo.
  • um corolário do ponto anterior: ao processar o texto em que você não sabe em qual conjunto de caracteres a entrada está escrita, mas pode assumir que é compatível com ASCII (como praticamente todos os conjuntos de caracteres). Por exemplo, grep '<.*>'procurar linhas contendo a <, >pair não funcionará se você estiver em um código de idioma UTF-8 e a entrada estiver codificada em um conjunto de caracteres de 8 bits de byte único como iso8859-15. Isso .ocorre porque apenas os caracteres correspondentes e caracteres não ASCII em iso8859-15 provavelmente não formarão um caractere válido no UTF-8. Por outro lado, LC_ALL=C grep '<.*>'funcionará porque qualquer valor de byte forma um caractere válido no Ccódigo do idioma.
  • A qualquer momento em que você processa dados de entrada ou dados de saída que não são destinados a / para humanos. Se estiver conversando com um usuário, convém usar a convenção e o idioma, mas, por exemplo, se você gerar alguns números para alimentar outro aplicativo que espera pontos decimais no estilo inglês ou nomes de meses em inglês, convém defina 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
    

    Isso também se aplica a coisas como comparação sem distinção entre maiúsculas e minúsculas (como em grep -i) e conversão de maiúsculas ( awk's toupper(), dd conv=ucase...). Por exemplo:

    grep -i i
    

    não é garantido que ele corresponda na Ilocalidade do usuário. Em algumas localidades turcas por exemplo, não faz como maiúsculas ié İ(note o ponto) lá e minúsculas Ié ı(observe o ponto em falta).


¹ Dependendo da codificação do texto, isso não é necessariamente a coisa certa a se fazer. Isso é válido para conjuntos de caracteres UTF-8 ou de byte único (como iso-8859-1), mas não necessariamente conjuntos de caracteres multibyte que não sejam UTF-8.

Por exemplo, se você estiver em uma zh_HK.big5hkscslocalidade (Hong Kong, usando a variante Hong Kong da codificação de caracteres chineses BIG5) e desejar procurar letras em inglês em um arquivo codificado nesses conjuntos de caracteres, faça o seguinte:

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

ou

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

estaria errado, porque nesse conjunto de caracteres (e muitos outros, mas pouco usado desde que o UTF-8 foi lançado), muitos caracteres contêm bytes que correspondem à codificação ASCII dos caracteres A-Za-z. Por exemplo, todos A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(e muitos mais) contêm a codificação de A. é 0x96 0x41 e Aé 0x41 como em ASCII. Portanto, o nosso LC_ALL=C grep '[a-zA-Z]'corresponderia às linhas que contêm esses caracteres, pois interpretaria mal essas seqüências de bytes.

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

funcionaria, mas somente se LC_ALLnão estiver definido de outra forma (o que substituiria LC_COLLATE). Então você pode ter que fazer:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

se você quiser procurar letras em inglês em um arquivo codificado na codificação da localidade.

Stéphane Chazelas
fonte
12
+1, é a melhor resposta (para apontar a substituição, etc.). Mas faltam os exemplos (agradáveis) de resposta de Ignacio ^^
Olivier Dulac
1
Uma pequena nitpick: a Clocalidade é necessária apenas para suportar o "conjunto de caracteres portátil" (ASCII 0-127), e o comportamento para caracteres> 127 é tecnicamente não especificado . Na prática, a maioria dos programas os trata como dados opacos e os repassa como você descreveu. Mas não todos: em particular, Ruby pode engasgar com dados de caracteres com bytes> 127 se estiver executando no Clocal. Sinceramente, não sei se isso é tecnicamente "compatível", mas já vimos isso na natureza .
Andrew Janke
2
@AndrewJanke, sim. Observe que o conjunto de caracteres portáteis não implica ASCII nem 0-127. Houve muita discussão na lista de discussão do grupo de Austin sobre quais seriam as propriedades do conjunto de caracteres do código de idioma "C" e o consenso geral (e que será esclarecido na próxima especificação) é que esse conjunto de caracteres seja único byte e abrangem toda a faixa de 8 bits (com as propriedades descritas aqui). Enquanto isso, sim, pode haver alguma divergência (como bug ou porque a especificação não é explícita o suficiente). Em qualquer caso, LC_ALL = C é o mais próximo que você pode obter um comportamento sensato.
Stéphane Chazelas
1
Um ponto de código Unicode em UTF-8 pode ter no máximo 4 octetos (ou bytes), mas alguns caracteres precisam de mais de um ponto de código, o que pode levar a seqüências mais longas que 6 octetos.
12431234123412341234123
1
@ 12431234123412341234123, a codificação UTF-8 original cobre até U + 7FFFFFFF (6 bytes, e existem algumas extensões para até 13 bytes como perl's \x{7FFFFFFFFFFFFFFF}) e enquanto o intervalo de pontos de código Unicode foi arbitrariamente restrito a U + 10FFFF (devido à limitação de design UTF-16), algumas ferramentas ainda reconhecem / produzem caracteres de 6 bytes. Isso é o que eu quis dizer com caracteres de 6 bytes. Na semântica do Unix, um caractere é um ponto de código. Seus mais de um "código" de ponto de código são geralmente referenciados como clusters de graphem para desambiguar os caracteres.
Stéphane Chazelas
7

Cé o código de idioma padrão, "POSIX" é o alias de "C". Eu acho que "C" é derivado de ANSI-C. Talvez o ANSI-C defina o código do idioma "POSIX".

Edward Shen
fonte
Tanto o C quanto o UNIX são muito anteriores ao ANSI C.
um CVn
@ MichaelKjörling: Então? Eu já vi documentação pré-ANSI e não tinha localidades. Internamente na AT&T Bell Labs, todos falavam inglês.
precisa saber é o seguinte
@MSalters O fato de a documentação pré-ANSI para o idioma C não mencionar localidades (o que pode ou não implicar que o pré-ANSI, C não tinha conceito de localidades; afinal, tenho certeza de que o idioma ainda não , mas isso não vem ao caso) não implica que o Cnome da localidade derive de "ANSI C".
um CVn 22/08/13
2
@ MichaelKjörling: Você está perdendo o objetivo. Quando as localidades foram introduzidas, "C" já significava "ANSI C". Que isso significou K&R C no passado é irrelevante.
MSalters
3

Até onde eu sei, o OS X usa a ordem de intercalação de pontos de código nos códigos de idioma UTF-8, portanto é uma exceção a alguns dos pontos mencionados na resposta de Stéphane Chazelas.

Isso imprime 26 no OS X e 310 no Ubuntu:

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

O código abaixo não imprime nada no OS X, indicando que a entrada está classificada. Os seis caracteres substitutos removidos causam um erro ilegal na sequência de bytes.

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

O código abaixo não imprime nada no OS X, indicando que não há dois pontos de código consecutivos (pelo menos entre U + 000B e U + D7FF) que tenham a mesma ordem de intercalação.

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

(Os exemplos acima são usados %bporque printf \\U25resultam em um erro no zsh.)

Alguns caracteres e sequências de caracteres que têm a mesma ordem de intercalação nos sistemas GNU não têm a mesma ordem de intercalação no OS X. Isso imprime ① primeiro no OS X (usando o OS X sortou GNU sort), mas Ubuntu primeiro no Ubuntu:

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

Isso imprime três linhas no OS X (usando o OS X sortou o GNU sort), mas uma linha no Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
nisetama
fonte
Alguém sabe por que existe essa diferença?
1.61803 23/02
3

Parece que LC_COLLATEcontrola a "ordem alfabética" usada por ls também. A localidade dos EUA classificará da seguinte maneira:

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

basicamente ignorando os períodos. Você pode preferir:

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

Eu certamente faço. Configuração LC_COLLATEpara Crealizar isso. Observe que ele também classificará minúsculas depois de todas as maiúsculas:

A.C
A.H
AFilename.C
a.C
a.H
SteveInCO
fonte