【问题标题】:Weird behavior of BASH glob/regex rangesBASH glob/regex 范围的奇怪行为
【发布时间】:2017-09-12 21:42:28
【问题描述】:

我看到 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'是一个例外:

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

【问题讨论】:

  • @Inian 我已将其添加到问题中
  • @anubhava 我知道,但事实并非如此。这就是我问这个问题的原因。
  • @Mat:这是一个非常好的匹配线程,但是这种行为无法通过 OP 的语言环境设置重现。
  • @Mat 谢谢你的链接。实际上,设置 LC_COLLATE=C 使其行为符合预期。但我仍然想知道为什么它与 LC_COLLATE="en_GB.UTF-8" 一样有效。有什么方法可以准确地知道特定 LC_COLLATE 值的指定范围内将包含哪些字符?

标签: regex bash shell cygwin glob


【解决方案1】:

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

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

在这种情况下使用POSIX 字符类,[[:upper:]] 或将您的locale 设置LC_ALLLC_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

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

[[ 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 1970-01-01
    • 2014-11-14
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多