关于18.04与20.04版本差异的问题(shell判断输入字母大小写)

sh/bash/dash/ksh/zsh等Shell脚本
回复
hansaw
帖子: 2
注册时间: 2020-12-16 12:48
系统: Windows

关于18.04与20.04版本差异的问题(shell判断输入字母大小写)

#1

帖子 hansaw » 2020-12-16 13:48

在Ubuntu18.04版本中,编写一个“判断输入字母大小写”的shell脚本,如下:
shell脚本.png
接下来输入不同字母判断程序正确性:
结果.png
可以发现大小写判断是错误的,原因是因为使用了中国地区的语言环境:
语言环境.png
语言环境.png (20.44 KiB) 查看 27243 次
这时候在代码中加入LC_ALL=C改变语言环境为C即可正确判断,新脚本:
新脚本.png
新结果:
新结果.png
在Ubuntu新版本20.04中,我依然是中国地区语言环境,但该脚本不需要添加LC_ALL=C也可以正确判断字母的大小写。
请问20.04版本修复了什么地方,导致脚本不添加LC_ALL=C也可以正确地运行?
头像
astolia
论坛版主
帖子: 6454
注册时间: 2008-09-18 13:11

Re: 关于18.04与20.04版本差异的问题(shell判断输入字母大小写)

#2

帖子 astolia » 2020-12-17 13:21

很好的问题。要找罪魁祸首的话,是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
hansaw
帖子: 2
注册时间: 2020-12-16 12:48
系统: Windows

Re: 关于18.04与20.04版本差异的问题(shell判断输入字母大小写)

#3

帖子 hansaw » 2020-12-17 14:14

astolia 写了: 2020-12-17 13:21 很好的问题。要找罪魁祸首的话,是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
原来是这样!非常感谢。 :-D
回复