“LC_ALL=C”有什么作用?

“LC_ALL=C”有什么作用?

Cfor 的值LC_ALL在类 Unix 系统中起什么作用?

我知道它强制所有方面使用相同的区域设置,但是有什么作用呢C

答案1

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。

localeC是一个特殊的 locale,它是最简单的 locale。您还可以说,其他语言环境适用于人类,而 C 语言环境适用于计算机。在 C 语言环境中,字符是单字节,字符集是 ASCII(当然,不是必需的,但实际上将在我们大多数人将使用的系统中),排序顺序基于字节值 1,语言通常是美国英语(尽管对于应用程序消息(与月份或日期名称或系统库的消息相对),它由应用程序作者自行决定)并且未定义货币符号等内容。

在某些系统上,与 POSIX 语言环境存在差异,例如未定义非 ASCII 字符的排序顺序。

通常,您运行带有 LC_ALL=C 的命令,以避免用户的设置干扰您的脚本。例如,如果要匹配从到 的[a-z]26 个 ASCII 字符,则必须设置。azLC_ALL=C

在 GNU 系统上,LC_ALL=Cand 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(在这方面mawk不是gawkPOSIX),它们不检查两个字符串是否相同,但它们排序是否相同。

  • 字符范围如grep.如果您想匹配用户语言中的字母,请使用grep '[[:alpha:]]'且不要修改LC_ALL。但如果你想匹配a-zA-ZASCII 字符,你需要LC_ALL=C grep '[[:alpha:]]'LC_ALL=C grep '[a-zA-Z]'²。匹配之前和之后[a-z]排序的字符(尽管对于许多 API,情况比这更复杂)。在其他地区,您通常不知道它们是什么。例如,某些语言环境会忽略排序的大小写,因此在某些 API(例如模式)中,可能会包含或。在许多 UTF-8 语言环境中(包括大多数系统),将包含拉丁字母从到带有变音符号,但不包括那些(因为在它们之前),我无法想象这会是你想要的(为什么你要包含并不是?)。az[a-z]bash[B-Z][A-Y]en_US.UTF-8[a-z]ayzzéź

  • 中的浮点运算ksh93ksh93尊重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

附带说明:,小数分隔符与,算术运算符冲突,这可能会导致更多混乱。

  • 当您需要将字符转换为字节时。如今,大多数语言环境都是基于 UTF-8 的,这意味着字符可以占用 1 到 6 个字节³。当使用文本实用程序处理字节数据时,您需要设置 LC_ALL=C。它还将显着提高性能,因为解析 UTF-8 数据是有成本的。

  • 上一点的推论:在处理文本时,您不知道输入是用什么字符集编写的,但可以假设它与 ASCII 兼容(几乎所有字符集都是如此)。例如,如果您处于 UTF-8 语言环境并且输入以单字节 8 位字符集(如 iso8859-15)编码,则grep '<.*>'查找包含<,对的行将不起作用。>这是因为.仅匹配字符,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
    

这也适用于不区分大小写的比较(如 in grep -i)和大小写转换(awk's toupper()dd conv=ucase...)。例如:

    grep -i i

I不保证在用户的区域设置中匹配。例如,在某些土耳其语言环境中,它并不像大写字母iİ注意点)和小写字母Iı注意缺少的点)。


笔记

再次强调,仅适用于基于 ASCII 的系统(绝大多数系统)。 POSIX 要求 C 语言环境的排序规则顺序为 ASCII 字符集中字符的顺序,即使在不允许在 C 语言环境中执行strcoll()===优化的 EBCDIC 系统上也是如此。strcmp()


² 根据文本的编码,这不一定是正确的做法。这对于 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,和AASCII 一样是 0x41。因此,我们LC_ALL=C grep '[a-zA-Z]'将匹配包含这些字符的那些行,因为它会误解这些字节序列。

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

会起作用,但前提是LC_ALL没有另外设置(这会覆盖LC_COLLATE)。所以你最终可能不得不这样做:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

如果您想在以语言环境编码编码的文件中查找英文字母。


³ 有些人会认为现在 Unicode 代码点(以及编码/解码 UTF-8 数据的库)已被任意限制为代码点 U+0000 到 U+10FFFF(不包括 0xD800 到 0xDFFF),因此现在它的长度是 1 到 4 个字节从 U+7FFFFFFF 向下以适应 UTF-16 编码,但某些应用程序仍然会愉快地编码/解码 6 字节 UTF-8 序列(包括落在 0xD800 .. 0xDFFF 范围内的序列)。

答案2

它强制应用程序使用默认语言进行输出:

$ 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

答案3

C是默认语言环境,“POSIX”是“C”的别名。我猜“C”源自 ANSI-C。也许 ANSI-C 定义了“POSIX”语言环境。

答案4

似乎LC_COLLATE也控制 ls 使用的“字母顺序”。美国区域设置将按如下方式排序:

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

基本上忽略了时期。您可能更喜欢:

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

我当然知道。设置LC_COLLATEC完成此操作。请注意,它还会在所有大写字母之后对小写字母进行排序:

A.C
A.H
AFilename.C
a.C
a.H

相关内容