为何bash的read读取Unicode字符数量断层?如何实现稳定读取?
问题原因与解决方案
为什么会出现字符数断层?
bash内置的read -n <count>命令里的count参数是按字节数统计的,而非Unicode字符数。你的MWE.txt里的·是Unicode字符(U+00B7),在UTF-8编码下占2个字节。
结合你的文本main · square来看:
- 前4个字符
main各占1字节,共4字节; - 紧跟的空格占1字节;
- 然后是
·,占2字节; - 后续字符均为单字节。
当你执行read -n 5时,bash会读取5字节,对应内容是main (4个Unicode字符),所以wc -m统计出的字符数是4,出现了断层。而当count是6时,bash尝试读取6字节,但因为·是2字节无法拆分,会自动读取到完整的·,此时读取的内容是main ·(6个Unicode字符),对应7字节,所以wc -m输出6。
本质原因:当指定的字节数落在多字节Unicode字符的字节区间内时,read会停止在完整字符的边界,导致实际读取的Unicode字符数和你指定的count不匹配。
如何稳定读取指定数量的Unicode字符?
以下是几种可行的方案:
方案1:用cut按Unicode字符截取
cut的-c选项是按Unicode字符计数的,直接用它截取指定数量的字符再处理:
#!/bin/bash char_count=$1 output=$(cut -c 1-"$char_count" MWE.txt) echo -n "$output" | wc -m
方案2:用mapfile读取指定字符数(bash 4.0+支持)
mapfile的-n参数是按Unicode字符计数的,适合读取内容:
#!/bin/bash char_count=$1 mapfile -n "$char_count" -t output < MWE.txt output_str="${output[*]}" echo -n "$output_str" | wc -m
方案3:利用bash字符串切片(UTF-8 locale下)
如果你的系统locale是UTF-8(绝大多数现代Linux系统都是),bash的字符串切片${var:0:n}会按Unicode字符截取:
#!/bin/bash char_count=$1 full_content=$(<MWE.txt) output="${full_content:0:$char_count}" echo -n "$output" | wc -m
用上述任意方案执行循环for x in $(seq 1 10 ); do ./MWE.sh $x; done,都会得到连续递增的1到10的输出。
内容的提问来源于stack exchange,提问作者merlin2011
相关产品推荐
相关产品推荐

