¿Qué hace "LC_ALL = C"?

324

¿Para qué sirve el Cvalor LC_ALLen sistemas tipo Unix?

Sé que obliga a la misma configuración regional para todos los aspectos, pero ¿qué hace C?

jcubic
fuente
Si desea resolver un problema con xclockadvertencia ( Missing charsets in String to FontSet conversion), será mejor si lo utiliza LC_ALL=C.UTF-8para evitar problemas con cirílico. Para establecer esta variable de entorno, debe agregar la siguiente línea al final del ~/.bashrcarchivo -export LC_ALL=C.UTF-8
fedotsoldier
@fedotsoldier probablemente deberías hacer una pregunta y dar la respuesta tú mismo, no creo que esté relacionado con la pregunta. Es solo la respuesta a un problema diferente que tienes.
jcubic
Sí, tienes razón, ok
fedotsoldier

Respuestas:

209

Obliga a las aplicaciones a usar el idioma predeterminado para la salida:

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

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

y obliga a la clasificación a ser byte-sabio:

$ 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
fuente
20
+1 para buenos ejemplos, pero carece de la información importante que está en la respuesta de Stephane ...
Olivier Dulac
44
¿Qué quiere decir por idioma predeterminado ?
Stéphane Chazelas
2
Sí, entiendo que el autor puede hacer lo que quiera, incluso no hacer lo que dice en la lata. La cosa es. El inglés de EE. UU. Es el único idioma que se puede representar correctamente con el juego de caracteres en LC_ALL = C, el único idioma donde tiene sentido el orden de clasificación en LC_ALL = C (LC_COLLATE), LC_ALL = C (LC_TIME) tiene nombres de mes y día en inglés. Nunca he visto aplicaciones donde LC_ALL = C devolvió el mensaje en un idioma diferente de LC_ALL = en LANGUAGE = en. Entonces, ¿tengo derecho a informar un error contra un programa si ese no es el caso? (no hablando de aplicaciones no traducidas al inglés aquí).
Stéphane Chazelas
2
El problema es "el inglés de EE. UU. Es el único idioma que se puede representar correctamente con el juego de caracteres en LC_ALL = C". Esto generalmente solo es cierto en los programas C / C ++ cuando se usan caracteres estrechos, pero incluso entonces hay excepciones (ya que hay varios lenguajes que solo usan caracteres y símbolos encontrados en ASCII). Informar un error cuando el idioma predeterminado no es el inglés te hará parecer ... intolerante.
Ignacio Vazquez-Abrams
3
Tenga en cuenta que en inglés (que significa LANG = en_US.utf8) los mensajes pueden (y deberían) usar caracteres unicode como "" para citar cadenas. Mientras que en LANG = C, solo tiene unos ASCII (comillas dobles, comillas inversas y apóstrofes).
Ángel
332

LC_ALLes la variable de entorno que anula todas las demás configuraciones de localización ( excepto $LANGUAGEen algunas circunstancias ).

Los diferentes aspectos de las localizaciones (como el separador de miles o el carácter de punto decimal, el conjunto de caracteres, el orden de clasificación, los nombres de mes, día, idioma o mensajes de aplicación como mensajes de error, símbolo de moneda) se pueden configurar utilizando algunas variables de entorno.

Por lo general, establecerá $LANGsu preferencia con un valor que identifica su región (como fr_CH.UTF-8si se encuentra en Suiza francófona, utilizando UTF-8). Las LC_xxxvariables individuales anulan un cierto aspecto. LC_ALLlos anula a todos. El localecomando, cuando se llama sin argumento, ofrece un resumen de la configuración actual.

Por ejemplo, en un sistema GNU, obtengo:

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

Puedo anular una configuración individual con, por ejemplo:

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

O:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

O anule todo con LC_ALL.

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

En un script, si desea forzar una configuración específica, ya que no sabe qué configuraciones ha forzado el usuario (posiblemente LC_ALL también), su mejor opción, la más segura y, en general, única es forzar LC_ALL.

La Cconfiguración regional es una configuración especial que debe ser la configuración regional más simple. También podría decir que si bien las otras configuraciones regionales son para humanos, la configuración regional C es para computadoras. En la configuración regional C, los caracteres son bytes individuales, el conjunto de caracteres es ASCII (bueno, no es obligatorio, pero en la práctica estará en los sistemas que la mayoría de nosotros usará), el orden de clasificación se basa en los valores de bytes, el idioma suele ser el inglés de EE. UU. (aunque para los mensajes de la aplicación (a diferencia de los nombres de mes o día o los mensajes de las bibliotecas del sistema), queda a discreción del autor de la aplicación) y los elementos como los símbolos de moneda no están definidos.

En algunos sistemas, hay una diferencia con la configuración regional POSIX donde, por ejemplo, el orden de clasificación para caracteres no ASCII no está definido.

Generalmente ejecuta un comando con LC_ALL = C para evitar que la configuración del usuario interfiera con su script. Por ejemplo, si desea [a-z]hacer coincidir los 26 caracteres ASCII de aa z, debe configurar LC_ALL=C.

En sistemas GNU, LC_ALL=Cy LC_ALL=POSIX(o LC_MESSAGES=C|POSIX) anular $LANGUAGE, mientras LC_ALL=anything-elseque no lo haría.

Algunos casos en los que normalmente necesita configurar LC_ALL=C:

  • sort -uo sort ... | uniq.... En muchos entornos locales distintos de C, en algunos sistemas (especialmente los GNU), algunos caracteres tienen el mismo orden de clasificación . sort -uno informa líneas únicas, sino una de cada grupo de líneas que tienen el mismo orden de clasificación. Entonces, si desea líneas únicas, necesita una configuración regional donde los caracteres son byte y todos los caracteres tienen un orden de clasificación diferente (lo que Cgarantiza la configuración regional).
  • lo mismo se aplica al =operador de POSIX compatible expru ==operador de POSIX compatible awks ( mawky gawkno son POSIX en ese sentido), que no comprueban si dos cadenas son idénticas sino si clasifican lo mismo.
  • Rangos de caracteres como en grep. Si quiere hacer coincidir una letra en el idioma del usuario, use grep '[[:alpha:]]'y no modifique LC_ALL. Pero si desea hacer coincidir los a-zA-Zcaracteres ASCII, necesita LC_ALL=C grep '[[:alpha:]]'o LC_ALL=C grep '[a-zA-Z]'¹. [a-z]coincide con los caracteres que se ordenan después ay antes z(aunque con muchas API es más complicado que eso). En otros lugares, generalmente no sabes cuáles son. Por ejemplo algunos locales ignoran caso para clasificar de manera [a-z]en algunas APIs como bashpatrones, podría incluir [B-Z]o [A-Y]. En muchos entornos locales UTF-8 (incluso en_US.UTF-8en la mayoría de los sistemas), [a-z]se incluirán las letras latinas de aa ycon diacríticos pero no los de z(ya queztipo antes que ellos) que no puedo imaginar que sea lo que quieres (¿por qué querrías incluirlo éy no ź?).
  • aritmética de coma flotante en ksh93. ksh93honra la decimal_pointpuesta en escena LC_NUMERIC. Si escribe un script que contiene a=$((1.2/7)), dejará de funcionar cuando lo ejecute un usuario cuya configuración regional tenga una coma 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
    

    Entonces necesitas cosas 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 nota al margen: el ,separador decimal entra en conflicto con el ,operador aritmético, lo que puede causar aún más confusión.

  • Cuando necesitas que los caracteres sean bytes. Hoy en día, la mayoría de las configuraciones regionales están basadas en UTF-8, lo que significa que los caracteres pueden ocupar de 1 a 6 bytes. Cuando se trata de datos que están destinados a ser bytes, con utilidades de texto, querrá establecer LC_ALL = C. También mejorará significativamente el rendimiento porque analizar datos UTF-8 tiene un costo.
  • Un corolario del punto anterior: cuando se procesa texto donde no se sabe en qué conjunto de caracteres está escrita la entrada, pero se puede suponer que es compatible con ASCII (como lo son prácticamente todos los conjuntos de caracteres). Por ejemplo, grep '<.*>'para buscar líneas que contengan un <, el >par no funcionará si está en un entorno local UTF-8 y la entrada está codificada en un conjunto de caracteres de 8 bits de un solo byte como iso8859-15. Esto se debe a que .solo los caracteres coincidentes y los caracteres no ASCII en iso8859-15 probablemente no formen un carácter válido en UTF-8. Por otro lado, LC_ALL=C grep '<.*>'funcionará porque cualquier valor de byte forma un carácter válido en la Cconfiguración regional.
  • Cualquier momento en el que procese datos de entrada o datos de salida que no estén destinados a / para un humano. Si está hablando con un usuario, es posible que desee usar su convención e idioma, pero, por ejemplo, si genera algunos números para alimentar alguna otra aplicación que espere puntos decimales en inglés o nombres de meses en inglés, querrá establecer 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
    

    Eso también se aplica a cosas como la comparación entre mayúsculas y minúsculas (como en grep -i) y la conversión de mayúsculas y minúsculas ( awk's toupper(), dd conv=ucase...). Por ejemplo:

    grep -i i
    

    no se garantiza que coincida Ien la configuración regional del usuario. En algunas configuraciones regionales turcas, por ejemplo, no lo hace, ya que las mayúsculas iestán İ(tenga en cuenta el punto) y las minúsculas Iestán ı(tenga en cuenta el punto que falta).


¹ Dependiendo de la codificación del texto, eso no es necesariamente lo correcto. Eso es válido para UTF-8 o juegos de caracteres de un solo byte (como iso-8859-1), pero no necesariamente para juegos de caracteres multibyte que no sean UTF-8.

Por ejemplo, si está en un zh_HK.big5hkscsentorno local (Hong Kong, utilizando la variante de Hong Kong de la codificación de caracteres chinos BIG5), y desea buscar letras en inglés en un archivo codificado en esos conjuntos de caracteres, haciendo lo siguiente:

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

o

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

estaría mal, porque en ese juego de caracteres (y muchos otros, pero apenas utilizado desde que salió UTF-8), muchos caracteres contienen bytes que corresponden a la codificación ASCII de caracteres A-Za-z. Por ejemplo, todos A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(y muchos más) contienen la codificación de A. es 0x96 0x41, y Aes 0x41 como en ASCII. Entonces nuestro LC_ALL=C grep '[a-zA-Z]'coincidiría en esas líneas que contienen esos caracteres, ya que malinterpretaría esas secuencias de bytes.

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

funcionaría, pero solo si LC_ALLno se establece de otra manera (lo que anularía LC_COLLATE). Entonces puede terminar teniendo que hacer:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

si desea buscar letras en inglés en un archivo codificado en la codificación de la configuración regional.

Stéphane Chazelas
fuente
12
+1, es la mejor respuesta (para señalar la anulación, etc.). Pero carece de los (bonitos) ejemplos de la respuesta de Ignacio ^^
Olivier Dulac
1
Un pequeño detalle: la Cconfiguración regional solo es necesaria para admitir el "juego de caracteres portátil" (ASCII 0-127), y el comportamiento de los caracteres> 127 no está técnicamente especificado . En la práctica, la mayoría de los programas los tratarán como datos opacos y los pasarán como usted describió. Pero no todos: en particular, Ruby puede ahogarse en los datos de caracteres con bytes> 127 si se ejecuta en la Cconfiguración regional. Sinceramente, no sé si eso es técnicamente "conforme", pero lo hemos visto en la naturaleza .
Andrew Janke
2
@ AndrewJanke, sí. Tenga en cuenta que el juego de caracteres portátil no implica ASCII ni 0-127. Se ha debatido mucho sobre la lista de correo del grupo de Austin sobre cuáles serían las propiedades del conjunto de caracteres de configuración regional "C" y el consenso general (y eso se aclarará en la siguiente especificación) es que ese conjunto de caracteres sería único. byte, y abarca el rango completo de 8 bits (con las propiedades descritas aquí). Mientras tanto, sí, puede haber alguna divergencia (como error o porque la especificación no es lo suficientemente explícita). En cualquier caso, LC_ALL = C es lo más cercano que puede obtener un comportamiento sensato.
Stéphane Chazelas
1
Un punto de código Unicode en UTF-8 puede tener un máximo de 4 octetos (o bytes), pero algunos caracteres necesitan más de un punto de código, lo que puede conducir a secuencias más largas de 6 octetos.
12431234123412341234123
1
@ 12431234123412341234123, la codificación UTF-8 original cubre hasta U + 7FFFFFFF (6 bytes, y hay algunas extensiones para subir hasta 13 bytes como perl's \x{7FFFFFFFFFFFFFFF}) y aunque el rango de puntos de código Unicode se ha restringido arbitrariamente a U + 10FFFF (debido a la limitación de diseño UTF-16), algunas herramientas aún reconocen / producen caracteres de 6 bytes. Eso es lo que quise decir con caracteres de 6 bytes. En la semántica de Unix, un carácter es un punto de código. Sus más de un "carácter" de punto de código se mencionan más generalmente como grupos de gráficos para desambiguarse de los caracteres.
Stéphane Chazelas
7

Ces la configuración regional predeterminada, "POSIX" es el alias de "C". Supongo que "C" se deriva de ANSI-C. Quizás ANSI-C defina la configuración regional "POSIX".

Edward Shen
fuente
Tanto C como UNIX son con mucho anteriores a ANSI C.
un CVn el
@ MichaelKjörling: ¿Entonces? He visto documentación anterior a ANSI y no tenía configuraciones regionales. Internamente en AT&T Bell Labs, todos hablaban inglés.
MSalters
@MSalters El hecho de que la documentación anterior a ANSI para el lenguaje C no menciona las configuraciones regionales (lo que puede implicar o no que antes de ANSI, C no tenía el concepto de configuraciones regionales; después de todo, estoy bastante seguro de que el lenguaje todavía no , pero eso no viene al caso) no implica que el Cnombre de la localidad se derive de "ANSI C".
un CVn
2
@ MichaelKjörling: Te estás perdiendo el punto. Cuando se introdujeron las configuraciones regionales, "C" ya significaba "ANSI C". Que significó K&R C en el pasado es irrelevante.
MSalters
3

Por lo que puedo decir, OS X usa el orden de clasificación de puntos de código en las configuraciones regionales UTF-8, por lo que es una excepción a algunos de los puntos mencionados en la respuesta de Stéphane Chazelas.

Esto imprime 26 en OS X y 310 en Ubuntu:

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

El siguiente código no imprime nada en OS X, lo que indica que la entrada está ordenada. Los seis caracteres sustitutos que se eliminan provocan un error de secuencia de bytes ilegal.

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

El siguiente código no imprime nada en OS X, lo que indica que no hay dos puntos de código consecutivos (al menos entre U + 000B y U + D7FF) que tengan el mismo orden de clasificación.

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

(Los ejemplos anteriores se usan %bporque printf \\U25resultan en un error en zsh).

Algunos caracteres y secuencias de caracteres que tienen el mismo orden de clasificación en los sistemas GNU no tienen el mismo orden de clasificación en OS X. Esto imprime ① primero en OS X (usando OS X sorto GNU sort) pero ② primero en Ubuntu:

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

Esto imprime tres líneas en OS X (usando OS X sorto GNU sort) pero una línea en Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
nisetama
fuente
¿Alguien sabe por qué hay esta diferencia?
1.61803
3

Parece que también LC_COLLATEcontrola el "orden alfabético" utilizado por ls. La configuración regional de EE. UU. Se ordenará de la siguiente manera:

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

básicamente ignorando los períodos. Quizás prefieras:

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

Ciertamente lo hago. Establecer LC_COLLATEpara Clograr esto. Tenga en cuenta que también ordenará minúsculas después de todas las mayúsculas:

A.C
A.H
AFilename.C
a.C
a.H
SteveInCO
fuente