Solución:
LC_ALL
es la variable de entorno que anula todas las demás configuraciones de localización (excepto $LANGUAGE
en algunas circunstancias).
Se pueden configurar diferentes aspectos de las localizaciones (como el separador de mil o el carácter de punto decimal, el conjunto de caracteres, el orden de clasificación, el mes, los nombres de los días, el idioma o los mensajes de la aplicación, como los mensajes de error, el símbolo de moneda) utilizando algunas variables de entorno.
Normalmente establecerás $LANG
según su preferencia con un valor que identifique su región (como fr_CH.UTF-8
si se encuentra en la Suiza francófona, utilizando UTF-8). El individuo LC_xxx
las variables anulan un cierto aspecto. LC_ALL
los anula a todos. los locale
comando, cuando se llama sin argumento, da 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 también LC_ALL), su mejor, más segura y generalmente única opción es forzar LC_ALL.
los C
locale es una configuración regional especial que está destinada a ser la configuración regional más simple. También podría decir que mientras que 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 juego de caracteres es ASCII (bueno, no es obligatorio, pero en la práctica estará en los sistemas que la mayoría de nosotros podremos 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 cosas como los nombres de los meses o días o los mensajes de las bibliotecas del sistema), queda a discreción del autor de la aplicación) y no se definen cosas como los símbolos de moneda.
En algunos sistemas, existe una diferencia con la configuración regional POSIX donde, por ejemplo, el orden de clasificación para los caracteres no ASCII no está definido.
Por lo general, ejecuta un comando con LC_ALL = C para evitar que la configuración del usuario interfiera con su script. Por ejemplo, si quieres [a-z]
para que coincida con los 26 caracteres ASCII de a
para z
, tienes que configurar LC_ALL=C
.
En los sistemas GNU, LC_ALL=C
y LC_ALL=POSIX
(o LC_MESSAGES=C|POSIX
) anular $LANGUAGE
, tiempo LC_ALL=anything-else
no lo haría.
Algunos casos en los que normalmente necesita configurar LC_ALL=C
:
-
sort -u
osort ... | uniq...
. En muchas configuraciones regionales distintas de C, en algunos sistemas (especialmente en GNU), algunos caracteres tienen el mismo orden de clasificación.sort -u
no 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 sean bytes y todos los caracteres tengan un orden de clasificación diferente (que elC
garantías locales). -
lo mismo se aplica a la
=
operador de POSIX compatibleexpr
o==
operador de POSIX compatibleawk
s (mawk
ygawk
no son POSIX en ese sentido), que no comprueban si dos cadenas son idénticas sino si se ordenan de la misma manera. -
Rangos de caracteres como en
grep
. Si quiere hacer coincidir una letra en el idioma del usuario, utilicegrep '[[:alpha:]]'
y no modifiquesLC_ALL
. Pero si quieres igualar ela-zA-Z
Caracteres ASCII, necesitasLC_ALL=C grep '[[:alpha:]]'
oLC_ALL=C grep '[a-zA-Z]'
².[a-z]
coincide con los caracteres que se ordenan despuésa
y antesz
(aunque con muchas API es más complicado que eso). En otros lugares, generalmente no sabe cuáles son. Por ejemplo, algunas configuraciones regionales ignoran mayúsculas y minúsculas para ordenar, por lo que[a-z]
en algunas API comobash
patrones, podría incluir[B-Z]
o[A-Y]
. En muchas configuraciones regionales UTF-8 (incluidasen_US.UTF-8
en la mayoría de los sistemas),[a-z]
incluirá las letras latinas dea
paray
con diacríticos pero no los dez
(ya quez
ordena antes que ellos) que no puedo imaginar que sea lo que quieres (¿por qué querrías incluiré
y noź
?). -
aritmética de punto flotante en
ksh93
.ksh93
honra aldecimal_point
instalándoseLC_NUMERIC
. Si escribe un guión que contienea=$((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; (([email protected])); }
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 ,
el separador decimal entra en conflicto con el ,
operador aritmético que puede causar aún más confusión.
-
Cuando necesita 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 deben ser bytes, con utilidades de texto, querrá establecer LC_ALL = C. También mejorará el rendimiento de manera significativa porque analizar los datos UTF-8 tiene un costo.
-
un corolario del punto anterior: al procesar texto en el que no sabe en qué juego de caracteres está escrita la entrada, pero puede asumir que es compatible con ASCII (como lo son prácticamente todos los juegos de caracteres). Por ejemplo
grep '<.*>'
para buscar líneas que contengan un<
,>
el par no funcionará si se encuentra en una configuración regional UTF-8 y la entrada está codificada en un conjunto de caracteres de 8 bits de un solo byte como iso8859-15. Eso es porque.
solo coincide con caracteres y es probable que los caracteres no ASCII en iso8859-15 no formen un carácter válido en UTF-8. Por otra parte,LC_ALL=C grep '<.*>'
funcionará porque cualquier valor de byte forma un carácter válido en elC
lugar. -
En cualquier momento en el que procese datos de entrada o datos de salida que no estén destinados a un ser 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 espera puntos decimales al estilo inglés o nombres de meses en inglés, querrá establecer LC_ALL = C:
$ printf '%gn' 1e-2 0,01 $ LC_ALL=C printf '%gn' 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 que no distingue entre mayúsculas y minúsculas (como en grep -i
) y conversión de casos (awk
‘s toupper()
, dd conv=ucase
…). Por ejemplo:
grep -i i
no se garantiza que coincida en I
en la configuración regional del usuario. En algunos lugares turcos, por ejemplo, no es tan mayúscula i
es İ
(nota el punto) allí y en minúscula I
es ı
(observe el punto que falta).
Notas
¹ nuevamente, solo en sistemas basados en ASCII (la inmensa mayoría de los sistemas). POSIX requiere que el orden de clasificación para la configuración regional C sea el del orden de los caracteres en el juego de caracteres ASCII, incluso en sistemas EBCDIC que no están autorizados a hacer lo mismo. strcoll()
=== strcmp()
optimización en la configuración regional C.
² Sin embargo, dependiendo de la codificación del texto, eso no es necesariamente lo correcto. Eso es válido para juegos de caracteres UTF-8 o 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ás en un zh_HK.big5hkscs
locale (Hong Kong, usando 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 se usa desde que salió UTF-8), muchos caracteres Contiene 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 A
es 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_ALL
no está configurado 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 con la codificación de la configuración regional.
³ Algunos dirían que es más bien de 1 a 4 bytes en estos días ahora que los puntos de código Unicode (y las bibliotecas que codifican / decodifican datos UTF-8) se han restringido arbitrariamente a puntos de código U + 0000 a U + 10FFFF (0xD800 a 0xDFFF excluidos) por debajo de U + 7FFFFFFF para acomodar la codificación UTF-16, pero algunas aplicaciones aún codificarán / decodificarán felizmente secuencias UTF-8 de 6 bytes (incluidas las que se encuentran en el rango 0xD800 .. 0xDFFF).
Obliga a las aplicaciones a utilizar 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 que la clasificación sea por bytes:
$ LC_ALL=en_US sort <<< $'anbnAnB'
a
A
b
B
$ LC_ALL=C sort <<< $'anbnAnB'
A
B
a
b
C
es 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”.