【问题标题】:How are Linux shells and filesystem Unicode-aware?Linux shell 和文件系统如何识别 Unicode?
【发布时间】:2016-12-21 05:58:47
【问题描述】:

我了解 Linux 文件系统将文件名存储为字节序列,这意味着与 Unicode 编码无关。

但是,UTF-8 或增强型 UTF-8 以外的编码很可能使用 0 字节作为可出现在文件名中的 Unicode 字符的多字节表示的一部分。在 Linux 文件系统 C 代码中的任何地方,您都以 0 字节终止字符串。那么Linux文件系统是如何支持Unicode的呢?它是否假设所有创建文件名的应用程序都只使用 UTF-8?但这不是真的,是吗?

同样,shell(例如 bash)在模式中使用 * 来匹配任意数量的文件名字符。我可以在 shell C 代码中看到它只是使用* 的 ASCII 字节并逐字节地分隔匹配。适合 UTF-8 编码的名称,因为它的属性是,如果您采用字符串的字节表示,则从开头匹配一些字节与 *,然后将其余的与另一个字符串匹配,然后是开头的字节实际上匹配一串完整的字符,而不仅仅是字节。

但其他编码不具有​​该属性,是吗?再说一遍,shell 是否假定 UTF-8?

【问题讨论】:

  • 我和 Google 都没有听说过“增强的 UTF-8”,它是什么?
  • @zwol 抱歉,应该是“修改后的 UTF-8”。
  • ...我也不知道那会是什么?
  • @zwol modified UTF-8 与 UTF-8 的区别在于 null 字符的编码方式 - 在 modified 中,它不是零字节,而是一个特定的两字节序列,这样,您可以有以零字节结尾的字符串。

标签: linux shell unicode utf-8


【解决方案1】:

确实,UTF-16 和其他“宽字符”编码不能用于 Linux 中的路径名(也不能用于任何其他 POSIX 兼容的操作系统)。

原则上不是正确的是,任何人都假设 UTF-8,尽管随着其他编码的消亡,将来可能会成为正确的。 Unix 风格的程序假定是 ASCII 兼容 编码。具有这些属性的任何编码都是 ASCII 兼容的:

  • 编码的基本单位是一个字节,而不是一个更大的实体。一些字符可能被编码为一个序列字节,但至少有127个字符只使用一个字节进行编码,即:
  • 由 ASCII 定义的字符(现在最好将其描述为 Unicode 代码点 U+000000 到 U+00007F,包括在内)被编码为单个字节,其值等于它们的 Unicode 代码点。
  • 相反,值为 0x00 到 0x7F 的字节必须始终解码为 ASCII 定义的字符,而不管周围的上下文如何。 (例如,字符串 0x81 0x2F 必须解码为两个字符,无论 0x81 解码为然后/。)

UTF-8 与 ASCII 兼容,但所有 ISO-8859-n 页面、EUC 编码以及许多其他编码也是如此。

某些程序可能还需要额外的属性:

  • 字符的编码,被视为字节序列,绝不是任何其他字符编码的正确前缀或正确后缀。

UTF-8 有这个属性,但(我认为)EUC-JP 没有。

也是许多“Unix 风格”程序保留代码点 U+000000 (NUL) 以用作字符串终止符的情况。这在技术上不是编码的限制,而是对文本本身的限制。 (与 byte 0x00 不出现在字符串中间的密切相关的要求是这个结果加上要求 0x00 映射到 U+000000 而不管周围的上下文。)

【讨论】:

  • 好吧...那么,让我们考虑一下dash,一个POSIX兼容的shell,当它的C代码尝试对包含*的模式进行文件名扩展时。假设我们有一个编码有两个非 ascii 字符,分别编码为 0x81 和 0x80 0x81。根据您的定义,这种编码很可能与 ASCII 兼容,因此 dash 应该可以工作。但在我看来,事实并非如此。模式<whatever 0x81 decodes to>*<whatever 0x81 decodes to>会匹配文件名<whatever 0x81 decodes to><whatever 0x80 0x81 decodes to>,这是错误的。
  • 我认为 Unicode 代码点没有“十六进制数字”。它们只是数字。所以你可以说,“值等于它们的 Unicode 代码点”。
  • @MarkGaleck 很可能是 dash 的 glob 算法在面对一些不是 UTF-8 的多字节 ASCII 兼容编码时存在缺陷;那将是我的“将来可能会成为现实”已经发生的情况。 (UTF-8 具有附加属性,即没有有效字符编码是另一个有效字符编码的正确前缀或正确后缀,这可以防止您的假设发生。我 认为 它可能发生在 EUC-JP ,但这是从维基百科上有些不清楚的描述中得出的;我无法构建一个可以触发它的文件。)
  • @zwol 谢谢我赞成你的回答,但我接受了另一个。我真的不明白你的措辞“某些程序可能还需要......”,你是说shell需要那个吗?这是在哪里记录的?而且您声明的附加属性仍然不够恕我直言 - 我已经在问题中自己写了正确的属性。
  • @MarkGaleck “没有字符的编码是另一个字符编码的正确前缀或后缀”意味着“如果您采用字符串的字节表示,则从开头匹配一些字节与 *,并匹配用另一个字符串休息,那么开头的字节实际上匹配一个完整字符的字符串,而不仅仅是字节。”因为“rest”只能在字符边界处开始匹配,并且字符边界是明确的。
【解决方案2】:

Linux 中没有文件名编码(无论如何在 ext 系列文件系统中)。文件名是字节序列,而不是字符。由应用程序将这些字节解释为 UTF-8 或其他任何内容。文件系统不在乎。

POSIX 规定shell obeys the locale environment vsriables such as LC_CTYPE 在执行模式匹配时。因此,仅比较字节而不考虑编码的模式匹配代码将与您的假设编码或任何有状态编码不兼容。但这似乎并不重要,因为现有语言环境通常不支持此类编码。另一方面,UTF-8 似乎得到了很好的支持:在我的实验中,bash 将文件名中的 ? 字符与单个 ​​Unicode 字符而不是单个字节正确匹配(给定 UTF-8 语言环境)为由 POSIX 规定。

【讨论】:

  • 可能要提一下,字节值 0x00 和 0x2F(以及其他字节值)在路径名中对内核很重要,并且不能出现在路径名 component 中。
  • @n.m.片段“与您的假设不兼容......现有语言环境通常不支持”这是我的答案,就在那里,谢谢!
猜你喜欢
  • 2015-05-27
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 2015-08-07
  • 1970-01-01
  • 2017-12-12
  • 2016-06-03
相关资源
最近更新 更多