有些符号占用两个字符单元。考虑这个脚本:
#!/usr/bin/env bash
echo '银^Htest'
echo 'а^Htest'
输出:
test
test
我如何知道全角符号的代码点?某种正则表达式?如何计算一个字符串占用了多少个字符单元?如何删除输出的所有内容?
如果这很重要的话我正在使用xterm
。
UPD为了给你更大的画面,我试图显示进度,输出一些信息,擦除它,再次输出......为此,我将光标移动到行的开头(\r
),擦除带有空格的行(tput cols
) ,然后再次移动光标 ( \r
)。但事实证明,该输出可能跨越几行。所以我决定计算字符数,向后移动(^H
),擦除(输出与字符串长度一样多的空格),然后再次向后移动(^H
)。
答案1
这实际上是四个问题:
- 我如何知道它们的代码点?
- 某种正则表达式?
- 如何计算一个字符串占用了多少个字符单元?
- 如何删除输出的所有内容?
OP 提到了 xterm,但只有最后两个可能特定于 xterm。
对于(1)和(2),echo命令没有太大帮助。你最好使用printf
,它识别反斜杠转义。在一些实现中(例如,GNU 核心工具),包括 Unicode 常量,例如,
printf '\u94f6\btest'
虽然为了常用表达,您最好还是使用 Perl(可以处理 UTF-8)等脚本语言。
进一步阅读:
- 如何在 Bash 中回显 4 位 Unicode 字符?
- printf命令
- 在 POSIX shell 脚本中嵌入 Unicode 字符的最佳方法是什么?
- Bash相当于Python的字符串文字,用于utf字符串转换
问题(3)和(4)更有趣。首先,脚本无法真正提前知道字符串需要多少个字符单元,而只能在事后测量它。这是因为宽度是基于终端和内核行为的组合。
- xterm 使用宽度来决定字符应该有多宽,其中存在一些有关“宽”(双宽)字体的问题,并且 wcwidth 的实现反映了开发人员对模糊宽度 Unicode 值的偏见。 xterm 可以配置(在运行时)使用 Markus Kuhn 的副本实施
wcwidth
;请注意,它可能不完整也不匹配实际的系统区域设置信息。 - 当 xterm 被告知擦除双角字符的一部分(如给定的示例中)时,它会用空格替换另一部分。大多数其他模仿 xterm 的终端都会这样做(尽管在快速检查中,我注意到一个终端只是简单地移动光标,导致宽字符和 ASCII 文本重叠)。如果你知道该值是双倍宽度,您可以简单地调整光标所在位置的概念。
- Linux 内核对
wcwidth
.自那时以来基于Linux的系统2004年有一个功能stty
叫做iutf8
:
它告诉内核输入是用 UTF-8 编码的,以便在规范输入模式下提供正确的编辑支持
- Linux 内核功能对于编辑很有用输入,因为当退格键删除前一个字符时,它可以帮助终端驱动程序执行一些合理的操作。然而,没有可比的功能输出。
你能,按照建议,使用光标位置报告(转义序列)来查找光标在不同点的位置。但如果你打算用它来决定如何清理线路,那么似乎更直接移动到打印 Unicode 值之前的位置,并从该点开始清除。
或者,您可以告诉终端在打印宽字符之前保存光标位置,然后恢复它(向后移动)。这可能看起来更干净、更可预测。恢复光标位置后,即可清除该行。所有这三个都可以使用转义序列来完成 - 或者tput
:
tput sc
printf '\u94f6'
tput rc
tput el
printf 'test'
除了作为演示之外,这确实有一个缺点,即终端只有一个保存的光标位置,并且为了确保清除单/双宽字符,它会清除整行。但光标最终会到达“正确”的位置。
- 获取字符串的显示宽度
- terminfo——终端能力数据库(对于
sc
、el
和rc
)