我在 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
位置x
并y
进行相同的排序,但由于括号表达式旨在匹配整理元素,这表明该bash
行为是预期的。
无论如何,我想[⅕-⅕]
或者至少[⅕-⅖]
应该匹配■
。
您会注意到不同的工具表现不同。 ksh93 的行为类似于bash
GNUgrep
或sed
不类似。其他一些 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 系统上工作,但不一定在其他系统上工作,因为它可能无法指定多字节字符的排序顺序。
其中的一个教训是平等在比较字符串时,这个概念并不像人们想象的那么清晰。平等可能意味着从最严格到最不严格。
- 字节数相同且所有字节组成部分具有相同的值。
- 字符数相同并且所有字符都相同(例如,引用当前字符集中的相同代码点)。
- 根据区域设置的排序算法,这两个字符串具有相同的排序顺序(即 a < b 和 b > a 都不为 true)。
现在,对于 2 或 3,假设两个字符串都包含有效字符。在 UTF-8 和其他一些编码中,某些字节序列不能形成有效字符。
因此,1 和 2 不一定等效,或者因为某些字符可能具有不止一种可能的编码。这通常是有状态编码的情况,例如 ISO-2022-JP,A
可以表示为41
or 1b 28 42 41
(1b 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