regex - BASH glob/regex 范围的奇怪行为

标签 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'到'z'的所有小写字母,似乎只有'a'是一个异常(exception):

$ 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 确实解决了这个问题。

最佳答案

肯定与您的区域设置设置有关。摘自GNU bash man page under Pattern Matching

[..] in the default C locale, [a-dx-z] is equivalent to [abcdxyz]. Many locales sort characters in dictionary order, and in these locales [a-dx-z] is typically not equivalent to [abcdxyz]; it might be equivalent to [aBbCcDdxXyYz], for example. To obtain the traditional interpretation of ranges in bracket expressions, you can force the use of the C locale by setting the LC_COLLATE or LC_ALL environment variable to the value C, or enable the globasciiranges shell option.[..]

在这种情况下使用 POSIX 字符类,[[:upper:]] 或更改您的 locale 设置 LC_ALL LC_COLLATEC 如上所述。

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

还有,在上面的locale设置下

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

但对于所有小写范围都是如此,

[[ a =~ [a-z] ]] ; echo $?
0
[[ b =~ [a-z] ]] ; echo $?
0

也就是说,所有这些都可以通过使用 POSIX 指定的字符类,在新的 shell 下没有任何 locale 设置来避免,

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

for l in {a..z}; do [[ $l =~ [[:upper:]] ]] || echo $l; done

关于regex - BASH glob/regex 范围的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43448655/

相关文章:

linux - 如何将参数传递给 source 命令调用的脚本?

linux - 需要检查文件是否存在?

java - 如何改进我的正则表达式(不起作用)

ios - 用于查找和替换数字的正则表达式

linux - 将字符串与数组连接起来以在 bash 中递归复制文件

bash - 在 Bash 中扩展变量中的变量

linux - 检查 git 提交是否是一天的第一天的脚本

C#:我应该如何转换以下内容?

Python使用正则表达式从文本中提取数字

linux - dpkg-dep 错误 : paste subprocess was killed by signal(Broken Pipe) Ubuntu WSL2