在类Unix系统中的C
价值是LC_ALL
什么?
我知道它在所有方面都强制使用相同的语言环境,但是该怎么C
做?
在类Unix系统中的C
价值是LC_ALL
什么?
我知道它在所有方面都强制使用相同的语言环境,但是该怎么C
做?
Answers:
它强制应用程序使用默认语言进行输出:
$ LC_ALL=es_ES man
¿Qué página de manual desea?
$ LC_ALL=C man
What manual page do you want?
并强制按字节排序:
$ 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
LC_ALL
是覆盖所有其他本地化设置的环境变量(在某些情况下除外$LANGUAGE
)。
可以使用一些环境变量来设置本地化的不同方面(例如千位分隔符或小数点字符,字符集,排序顺序,月份,日期名称,语言或应用程序消息,例如错误消息,货币符号)。
通常,您会$LANG
使用一个标识您所在地区的值来设置自己的偏好(例如,fr_CH.UTF-8
如果您使用UTF-8在瑞士法语中)。各个LC_xxx
变量会覆盖某个方面。LC_ALL
覆盖所有。该locale
命令在不带参数的情况下给出当前设置的摘要。
例如,在GNU系统上,我得到:
$ 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=
我可以使用以下方法覆盖单个设置:
$ LC_TIME=fr_FR.UTF-8 date
jeudi 22 août 2013, 10:41:30 (UTC+0100)
要么:
$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€
或使用LC_ALL覆盖所有内容。
$ LC_ALL=C LANG=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8 cat /
cat: /: Is a directory
在脚本中,如果要强制执行特定设置,因为您不知道用户强制执行了哪些设置(也可能是LC_ALL),则最好,最安全且通常唯一的选择是强制执行LC_ALL。
该C
区域是指是最简单的语言环境的特殊区域。您还可以说,其他语言环境适用于人类,而C语言环境适用于计算机。在C语言环境中,字符是单个字节,字符集是ASCII(不是必需的,但实际上将在我们大多数人都会使用的系统中使用),排序顺序基于字节值,该语言通常是美国英语(尽管对于应用程序消息(与月份或日期名称或系统库中的消息相反),它由应用程序作者自行决定),并且未定义货币符号之类的东西。
在某些系统上,POSIX语言环境有所不同,例如,未定义非ASCII字符的排序顺序。
通常,您使用LC_ALL = C运行命令,以避免用户的设置干扰您的脚本。举例来说,如果你想[a-z]
匹配的26个ASCII字符从a
到z
,你必须设置LC_ALL=C
。
在GNU系统上,LC_ALL=C
和LC_ALL=POSIX
(或LC_MESSAGES=C|POSIX
)重写$LANGUAGE
,而LC_ALL=anything-else
不会。
通常需要设置LC_ALL=C
以下几种情况:
sort -u
或sort ... | uniq...
。在C以外的许多语言环境中,在某些系统上(尤其是GNU语言),某些字符具有相同的排序顺序。sort -u
不会报告唯一行,但会报告具有相同排序顺序的每组行之一。因此,如果确实需要唯一的行,则需要一个字符为字节且所有字符具有不同排序顺序的C
语言环境(该语言环境保证)。=
运营商POSIX兼容的expr
或==
符合POSIX标准的运营商awk
S(mawk
并且gawk
没有在这方面POSIX),不检查两个字符串是否相同,但他们是否排序相同。grep
。如果您要匹配用户语言中的字母,请使用grep '[[:alpha:]]'
,不要修改LC_ALL
。但是,如果要匹配a-zA-Z
ASCII字符,则需要LC_ALL=C grep '[[:alpha:]]'
或LC_ALL=C grep '[a-zA-Z]'
¹。[a-z]
匹配之后a
和之前排序的字符z
(尽管使用许多API更为复杂)。在其他语言环境中,您通常不知道它们是什么。例如,某些语言环境会忽略大小写排序,因此[a-z]
在某些API(例如bash
模式)中,可能包含[B-Z]
或[A-Y]
。在许多UTF-8语言环境中(包括en_US.UTF-8
在大多数系统上),[a-z]
将包括变音符号从a
到的拉丁字母,y
但不包括z
(因为z
在它们之前排序),我无法想象这将是您想要的(为什么要包含é
而不是ź
?)。浮点数算法ksh93
。ksh93
荣誉的decimal_point
中设置LC_NUMERIC
。如果编写包含的脚本,a=$((1.2/7))
当其语言环境以逗号作为小数点分隔符的用户运行时,它将停止工作:
$ 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
然后,您需要类似以下内容:
#! /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
附带说明:,
小数点分隔符与,
算术运算符冲突,这可能会引起更多混乱。
grep '<.*>'
而寻找包含a的行,则该对将不起作用,并且输入使用单字节8位字符集(如iso8859-15)进行编码。这是因为在iso8859-15中仅匹配字符和非ASCII字符在UTF-8中可能不会形成有效字符。另一方面,它将起作用,因为任何字节值都在语言环境中形成有效字符。<
>
.
LC_ALL=C grep '<.*>'
C
任何时候处理非人为的/人为的输入数据或输出数据的时间。如果您正在与用户交谈,则可能要使用他们的约定和语言,但是,例如,如果您生成一些数字来提供其他一些期望使用英语小数点或英语月份名称的应用程序,则需要设置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
这也适用于不区分大小写的比较(如grep -i
)和大小写转换(awk
的toupper()
,dd conv=ucase
...)。例如:
grep -i i
不能保证I
在用户的语言环境中匹配。在比如一些土耳其语言环境,它没有为大写的i
是İ
(注意点)有和小写I
是ı
(注意漏点)。
¹根据文本的编码,这不一定是正确的选择。这对于UTF-8或单字节字符集(例如iso-8859-1)有效,但不一定非UTF-8多字节字符集。
例如,如果您在zh_HK.big5hkscs
语言环境中(香港,使用BIG5中文字符编码的香港变体),并且想要在以该字符集编码的文件中查找英文字母,请执行以下任一操作:
LC_ALL=C grep '[[:alpha:]]'
要么
LC_ALL=C grep '[a-zA-Z]'
会是错误的,因为在该字符集中(还有许多其他字符集,但自UTF-8出现以来就很少使用),许多字符包含的字节对应于A-Za-z字符的ASCII编码。例如,所有A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽
(还有更多)都包含的编码A
。䨝
是0x96 0x41,并且A
是0x41(类似于ASCII)。因此,我们LC_ALL=C grep '[a-zA-Z]'
将在包含那些字符的那些行上进行匹配,因为这会误解那些字节序列。
LC_COLLATE=C grep '[A-Za-z]'
会起作用,但前提LC_ALL
是没有另外设置(它将覆盖LC_COLLATE
)。因此,您可能最终不得不做:
grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'
如果要在以语言环境编码编码的文件中查找英文字母。
C
仅要求语言环境支持“便携式字符集”(ASCII 0-127),并且chars> 127的行为在技术上未指定。实际上,大多数程序会将它们视为不透明数据,并按照您所描述的那样传递它们。但不是全部:特别是,如果在C
语言环境中运行,Ruby可能会阻塞字节数大于127的char数据。老实说,我不知道这在技术上是否“符合标准”,但是我们已经在野外看到了。
perl
的\x{7FFFFFFFFFFFFFFF}
),而Unicode代码点的范围已被任意限制为U + 10FFFF (由于UTF-16设计限制),某些工具仍可以识别/产生6个字节的字符。这就是6字节字符的意思。在Unix语义中,一个字符是一个代码点。您的多个代码点“字符”通常被称为字素簇,以消除字符歧义。
C
是默认语言环境,“ POSIX”是“ C”的别名。我猜“ C”是从ANSI-C派生的。也许ANSI-C定义了“ POSIX”语言环境。
C
语言环境名称是从“ ANSI C”派生的。
据我所知,OS X在UTF-8语言环境中使用代码点排序规则,因此,这是StéphaneChazelas答案中提到的某些点的例外。
这在OS X中打印26,在Ubuntu中打印310:
export LC_ALL=en_US.UTF-8
printf %b $(printf '\\U%08x\\n' $(seq $((0x11)) $((0x10ffff))))|grep -a '[a-z]'|wc -l
下面的代码在OS X中不显示任何内容,表示输入已排序。删除的六个代理字符会导致非法字节序列错误。
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
下面的代码在OS X中不打印任何内容,表示没有两个连续的代码点(至少在U + 000B和U + D7FF之间)具有相同的排序顺序。
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
(上面的示例使用%b
是因为printf \\U25
在zsh中导致错误。)
GNU系统中具有相同排序顺序的某些字符和字符序列在OS X中没有相同的排序顺序。这在OS X中先打印①(使用OS X sort
或GNU sort
),而在Ubuntu中先打印②:
export LC_ALL=en_US.UTF-8;printf %s\\n ② ①|sort
这将在OS X(使用OS X sort
或GNU sort
)中打印三行,而在Ubuntu中打印一行:
export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
xclock
warning(Missing charsets in String to FontSet conversion
)解决问题,则最好使用该方法LC_ALL=C.UTF-8
来避免西里尔字母的问题。要设置此环境变量,必须在~/.bashrc
文件末尾添加以下行export LC_ALL=C.UTF-8