聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

bash多字节处理源码分析

2020-01-27 05:05 浏览: 1850 次 我要评论(0 条) 字号:

bash-4.4

研究题目: bash中有时候输入的汉字不能显示,或者有的是显示了,但是不能使用光标,光标走过汉字时,汉字就坏了。

相关文章: bash 中文汉字乱码问题

前面的研究文章中提到,当正确的设置了LANG编码时,bash是可以正确处理汉字的,但是,今天遇到的情况是,bash进程的环境变量LANG=zh_CN.UTF-8 是没问题的,但是,依然不能正确处理汉字,而且不是能显示汉字,光标走过汉字时,汉字坏掉;而是,汉字根本就不显示,strace结果如下:

我输入的是’汉’字,读入到第二个字节的时候,发现不对了,往标准错误写了一些错误消息,这个错误消息从源码上来看,是通过 _rl_arg_getchar() 输出的。

 

正常来讲,bash是停留在 下面代码处等待输入的:

从函数名可知(实际调试也是如此),该函数只读取一个字节,对于多字节字符来讲,该函数执行多次,正常来讲,多字节字符被完全读入之前,是不会被回显的,至于该多字节字符由几个字节组成的,这个问题不是该函数来处理的,而是用函数: rl_insert() 来处理的。

正常情况下等待输入时的调用栈如下;

当readline_internal_char通过rl_read_key -> rl_getc 读到一个字节时,就将该字节交给_rl_dispatch -> _rl_dispatch_subseq 来处理,当前如果处于bash的插入模式,则交由rl_insert 函数来处理,如下:

每次read一个字符是函数 readline_internal_char() 发起的,但是,具体需要读入几个字节算一个字符,是函数rl_insert() 中判断的,并根据判断结果来调用适当次数的rl_read_key()函数。而rl_insert函数也是bash在insert模式下调用的函数,(如果是替换模式就是函数_rl_overwrite_char()了 ?)。

rl_insert 中调用了 _rl_insert_char() 可以判断还有几个字节需要读入。

从MB_LEN_MAX 常量来看,多字节字符最少支持16字节的字符的。

bash编译时,需要定义 HANDLE_MULTIBYTE 常量,才能支持多字节的,如何判断bash是否支持多字节呢?

strings /bin/bash |grep _rl_read_mbstring

如果存在该字符串,基本就算是支持了,因为该函数在 #if defined (HANDLE_MULTIBYTE) 中定义的;然而,真正本次讨论的问题,和该函数并无关系。

关于多字节的很多逻辑都在下面文件中:

当需要读入一个完整的多字节时,将多字节的内容缓存到pending_bytes数组中,用pending_bytes_length 来记录已读入字节的长度,通过函数 mbrtowc 来判断输入字符是否完整,该函数并不属于bash,(至此,似乎和bash本身没啥关系了)。

参考: man mbrtowc ;该man页中最下面有个NOTE:

The behavior of mbrtowc() depends on the LC_CTYPE category of the current locale.

然后,我们试试:

# export LC_CTYPE=zh_CN.UTF-8
bash: warning: setlocale: LC_CTYPE: cannot change locale (zh_CN.UTF-8)

参考: https://www.cnblogs.com/kevingrace/p/8191929.html

因为我使用的是ubuntu容器,里面没有中文相关软件包,locale -a查看如下:

如果仅仅是为了让bash能正确处理汉字,其实,设置正确的编码已经足够,如上图:

export LC_CTYPE=C.UTF-8

export LC_ALL=C.UTF-8

至于如何才能正确支持 LC_CTYPE=zh_CN.UTF-8 或者 LC_CTYPE=en_US.UTF-8 ,稍后研究

看来LC_ALL不是一个简单的变量,不管export与否,bash都知道这是一个需要特殊处理的变量。虽然该变量的设置是能被bash察觉的,但是依然不能影响当前bash的编码。

虽然这里看似设置错误了,实际上,变量赋值时成功的:

 



网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复