括号表达式(不带范围)与 bash 中的意外字符匹配

括号表达式(不带范围)与 bash 中的意外字符匹配

我在 Linux 上使用 bash。我从以下 if 语句中获得了成功,但这不应该返回失败代码吗?

if [[ ■ = [⅕⅖⅗] ]] ; then echo yes ; fi

该正方形不等于任何字符,所以我不明白为什么我会得到成功代码。

对我来说,将双括号保留在我的盒子里很重要。

在这种情况下还有其他方法可以实现范围,或者还有其他建议吗?

答案1

这是这些字符具有相同排序顺序的结果。

你还会注意到

sort -u << EOF
EOF

仅返回一行。

或者那个:

expr ■ = ⅕

返回 true(按照 POSIX 的要求)。

大多数 GNU 系统附带的语言环境都有许多具有相同排序顺序的字符(甚至字符序列(整理序列))。对于那些■⅕⅖⅗,这是因为未定义顺序,而那些未定义顺序的字符最终在GNU 系统中具有相同的排序顺序。有些字符被明确定义为具有相同的排序顺序,例如 ş 和 Ş(尽管没有明显的(无论如何对我来说)真正的逻辑或如何完成的一致性)。

这就是令人惊讶和虚假行为的根源。我有最近在奥斯汀小组上提出了这个问题(POSIX 和单一 UNIX 规范背后的主体)邮件列表,截至 2015-04-03,讨论仍在进行中。

在这种情况下,我不清楚是否[y]应该匹配x位置xy进行相同的排序,但由于括号表达式旨在匹配整理元素,这表明该bash行为是预期的。

无论如何,我想[⅕-⅕]或者至少[⅕-⅖]应该匹配

您会注意到不同的工具表现不同。 ksh93 的行为类似于bashGNUgrepsed不类似。其他一些 shell 有不同的行为,有些yash甚至更多错误。

为了获得一致的行为,您需要一个所有字符都以不同方式排序的语言环境。 C 语言环境是典型的语言环境。然而,大多数系统上 C 语言环境中的字符集是 ASCII。在 GNU 系统上,您通常可以访问C.UTF-8可用于处理 UTF-8 字符的语言环境。

所以:

(export LC_ALL=C.UTF-8; [[ ■ = [⅕⅖⅗] ]])

或等效标准:

(export LC_ALL=C.UTF-8
 case ■ in ([⅕⅖⅗]) true;; (*) false; esac)

应该返回 false。

另一种替代方法是仅设置LC_COLLATE为 C,它可以在 GNU 系统上工作,但不一定在其他系统上工作,因为它可能无法指定多字节字符的排序顺序。


其中的一个教训是平等在比较字符串时,这个概念并不像人们想象的那么清晰。平等可能意味着从最严格到最不严格。

  1. 字节数相同且所有字节组成部分具有相同的值。
  2. 字符数相同并且所有字符都相同(例如,引用当前字符集中的相同代码点)。
  3. 根据区域设置的排序算法,这两个字符串具有相同的排序顺序(即 a < b 和 b > a 都不为 true)。

现在,对于 2 或 3,假设两个字符串都包含有效字符。在 UTF-8 和其他一些编码中,某些字节序列不能形成有效字符。

因此,1 和 2 不一定等效,或者因为某些字符可能具有不止一种可能的编码。这通常是有状态编码的情况,例如 ISO-2022-JP,A可以表示为41or 1b 28 42 411b 28 42是切换到 ASCII 的序列,您可以根据需要插入任意数量的编码,这不会产生任何影响),尽管我不会期望这些类型的编码仍在使用中,并且 GNU 工具至少通常无法与它们正常工作。

另请注意,大多数非 GNU 实用程序无法处理 0 字节值(ASCII 中的 NUL 字符)。

其中哪一个定义使用取决于实用程序和实用程序实现或版本。 POSIX 对此并不是 100% 清楚。在 C 语言环境中,所有 3 个都是等效的。在那 YMMV 之外。

答案2

你做错了,=而且==不一样。

试试这些例子:

if [[ "■" == "[⅕⅖⅗]" ]] ; then echo yes ; else echo no ; fi

if [[ "1" == "1" ]] ; then echo yes ; else echo no ; fi

if [[ "■" == "■" ]] ; then echo yes ; else echo no ; fi

相关内容