很好的问题。要找罪魁祸首的话,是glibc库。
bash在处理[x-y]这种东西时,做范围判断用的是c库函数strcoll,而strcoll做字符串比较时,会受到LC_COLLATE环境变量的影响,根据LC_COLLATE定义的语言来决定字符的顺序。
LC_COLLATE只设置了语言,在指定语言中各个字符的具体顺序,是存在另外的文件中的。以zh_CN为例,strcoll在比较时,会去检查/usr/share/i18n/locale/zh_CN中LC_COLLATE和END LC_COLLATE之间的定义。可以看到里面是句copy "iso14651_t1_pinyin",也就是和同目录下iso14651_t1_pinyin文件中定义的内容一样。同样地,iso14651_t1_pinyin又参照了iso14651_t1_common的内容。而正是iso14651_t1_common在18.04和20.04中的不同导致了你的问题。
你在18.04的iso14651_t1_common文件中,可以找到这样的一段
代码: 全选
collating-symbol <a>
collating-symbol <b>
collating-symbol <c>
...
正是这些定义语句,把事情搞砸了。
collating-symbol在这里的意思是把后面的字符
组视为同一个顺序号。<a>组里包括了小写的a和大写的A,还有些其他a的变体,比如à。strcoll在做比较时,就会把a、A、à这些都视为相同的东西。这样一来,a自然也在[A-Z]的范围里了。
当你设置LC_ALL=C时,LC_COLLATE也一并设置为了C,/usr/share/i18n/locale/C里LC_COLLATE段定义的顺序,就是字符的unicode编码,所以strcoll就会认为a和A是不同的东西,a就不会在[A-Z]的范围里。
18.04里glibc的版本是2.27,20.04里glibc的版本是2.31,这个问题是在glibc 2.28里修正的。具体可见2.28发布日志Major new features的第一条
https://sourceware.org/legacy-ml/libc-a ... 00002.html