BASH glob / regex范围的奇怪行为

时间:2017-04-17 09:23:26

标签: regex bash shell cygwin glob

我看到BASH支架范围(例如[A-Z])以不正常的方式表现。
是否有对这种行为的解释,或者这是一个错误?

我们说我有一个变量,我想从中删除所有大写字母:

$ var='ABCDabcd0123'
$ echo "${var//[A-Z]/}"

我得到的结果是:

a0123

如果我使用sed,我会得到预期的结果:

$ echo "${var}" | sed 's/[A-Z]//g'
abcd0123

BASH内置正则表达式匹配似乎也是如此:

$ [[ a =~ [A-Z] ]] ; echo $?
1
$ [[ b =~ [A-Z] ]] ; echo $?
0

如果我检查' a'中的所有小写字母?到了',似乎只有' a'是一个例外:

$ for l in {a..z}; do [[ $l =~ [A-Z] ]] || echo $l; done
a

我没有启用不区分大小写的匹配,即使我这样做了,也不应该写字母' a'行为不同:

$ shopt -p nocasematch
shopt -u nocasematch

作为参考,我使用的是Cygwin,而且我在其他任何机器上都看不到这种行为:

$ uname
CYGWIN_NT-6.3
$ bash --version | head -1
GNU bash, version 4.3.46(7)-release (x86_64-unknown-cygwin)
$ locale
LANG=en_GB.UTF-8
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_ALL=

修改

我发现此处报告的问题完全相同: https://bugs.launchpad.net/ubuntu/+source/bash/+bug/120687
所以,我猜它是" en_GB.UTF-8"的错误(?)整理,但不是BASH本身 设置LC_COLLATE=C确实解决了这个问题。

1 个答案:

答案 0 :(得分:3)

当然与设置locale有关。摘自GNU bash man page under Pattern Matching

  默认C区域设置中的

[..],[a-dx-z]相当于[abcdxyz]。许多语言环境按字典顺序对字符进行排序,在这些语言环境中[a-dx-z]通常不等同于[abcdxyz];例如,它可能等同于[aBbCcDdxXyYz]。要获得括号表达式中范围的传统解释,可以通过将LC_COLLATELC_ALL环境变量设置为值C来强制使用C语言环境,或启用{{ 1}} shell选项。[..]

在这种情况下使用globasciiranges字符classess,POSIX或将[[:upper:]]设置localeLC_ALL更改为LC_COLLATE,如上所述上方。

C

此外,在设置此区域设置因此打印字母时,对于所有小写字母执行大写检查的否定测试将失败,

LC_ALL=C var='ABCDabcd0123'
echo "${var//[A-Z]/}"
abcd0123

此外,在上述区域设置

LC_ALL=C; for l in {a..z}; do [[ $l =~ [A-Z] ]] || echo $l; done

但对于所有小写范围

都是如此
[[ a =~ [A-Z] ]] ; echo $?
1
[[ b =~ [A-Z] ]] ; echo $?
1

这样说,所有这些都可以通过在新的shell 下使用[[ a =~ [a-z] ]] ; echo $? 0 [[ b =~ [a-z] ]] ; echo $? 0 指定的字符类来避免,而不用任何POSIX设置,

locale

echo "${var//[[:upper:]]/}"
abcd0123