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


20

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

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

正方形不等于任何字符,所以我看不到为什么我得到一个成功代码。

对于我来说,保持双括号很重要。

在这种情况下,还有其他方法可以做一个范围吗,或者还有什么其他建议?


2
可能是所有这些字符在您的语言环境中具有未定义的排序顺序的结果(因此对其进行了排序)。请参阅奥斯汀小组正在进行的相关讨论将语言环境更改为C可以修复它
斯特凡Chazelas

1
抱歉,C这里不是单字节字符,所以不会在这里做。C.UTF-8将在可用的地方做。
斯特凡Chazelas

11
恭喜,您成功召集史黛芬(Stéphane),在第一个问题上挥舞着奥斯丁小组的话题。至少要有一个互联网才值得。或⅘甚至■互联网,显然它们是相同的。欢迎使用Unix&Linux,请继续提出有趣的问题。
derobert

Answers:


29

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

您还会注意到

sort -u << EOF




EOF

仅返回一行。

或者那个:

expr  = 

返回true(根据POSIX的要求)。

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

这就是令人惊讶和虚假行为的根源。我最近在Austin组(POSIX和Single UNIX Specification背后的机构)邮件列表中提出了这个问题,并且直到2015-04-03讨论仍在进行中。

在这种情况下,我不清楚是否[y]应该x在何处进行匹配x以及y在何处进行排序,但是我不清楚,但是由于方括号表示要与归类元素进行匹配,因此这表明了bash预期的行为。

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

您会注意到,不同的工具的行为有所不同。ksh93的行为类似于bash,GNU grepsed不。其他一些外壳具有不同的行为,例如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都不为真)。

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

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

还要注意,大多数非GNU实用程序不能处理0字节值(ASCII中的NUL字符)。

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


1和2不同的另一个常见情况是在Unicode中带有诸如组合字符之类的东西。
吉尔斯(Gilles)'所以

@Gilles,组合字符是它们自己的字符。组合形成一个字素/单元格,但仍由几个字符组成。é(U + 00E9)和é(e后跟U + 0301)是相同的字素,但是有两个不同的字符序列(至少从POSIX API角度来看)。通过1和2,它们将有所不同。如果将U + 0301的所有排序规则权重都设置为“ IGNORE”,那么到3点,他们可以认为是相同的,但是通常情况并非如此,因为通常要确定变音符号的顺序。
斯特凡Chazelas

通常希望考虑é使用相同的字符串,但不要使用相同的字符串e。POSIX的排序顺序概念很少是正确的,它过于依赖于字符,并且不能解释大多数常见的字符串排序方式(例如,法语词典不使用字典顺序来对单词进行排序:他们进行了首个字典顺序,忽略了重音,然后使用重音来确定联系)。
吉尔(Gilles)'所以

@吉尔斯,是的。这就是为什么我要说在glibc语言环境中具有相同排序顺序(故意)的那些字符毫无意义。通常通过首先对字符串进行一些转换来解决évsé,例如规范分解(类似于您要进行不区分大小写的排序/匹配时首先转换为小写)。另请参阅ICU指南以获取有关此主题的一些良好参考。
斯特凡Chazelas

@ Gilles,POSIX语言环境归类算法中的权重可以完成法语字典排序。这就是权重的工作方式。第一遍使用主权重(其中E和E(以及E和E)具有相同的和组合重音符被忽略)的第二通(如果相等)检查口音,一个第三通大写...
斯特凡Chazelas

-3

您做错了,=并且==不一样。

尝试以下示例:

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

1
这不是真的。POSIX指定=应使用运算符检查相等性。问题是缺少引号,而不是运算符。
scai 2015年

1
man bash在该[[节中也要说:“ =运算符等于==。”
2015年

1
@ scai,POSIX未指定[[...]]运算符。和=和==在shell中实现相同(ksh / bash / zsh)并且用于模式匹配,而不是相等。
斯特凡Chazelas

与模式进行比较时,模式不得加引号,否则将其视为文字字符串,因此在第一个测试中为“ no”。
xhienne
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.