如何在Bash提示符中正确地转义Unicode字符

9

我有一种特定的bash提示符方法,让我们假设它看起来像这样:

CHAR="༇ "
my_function="
    prompt=\" \[\$CHAR\]\"
    echo -e \$prompt"

PS1="\$(${my_function}) \$ "

为了解释上述问题,我正在通过执行存储在字符串中的函数来构建我的Bash提示符,这是根据这个问题得出的决定。假装它可以很好地工作,因为它确实可以,除非涉及Unicode字符。
我正在尝试找到适当的方法来转义Unicode字符,因为现在它会影响Bash行长度。测试是否出错的简单方法是输入一个长命令,执行它,按CTRL-R查找它,然后按CTRL-A CTRL-E跳转到行的开头/结尾。如果文本混乱,则表示未正常工作。
我已经尝试了多种方法来正确转义函数字符串中的Unicode字符,但似乎没有任何方法可以奏效。
像这样的特殊字符可用:
COLOR_BLUE=$(tput sgr0 && tput setaf 6)

my_function="
    prompt="\\[\$COLOR_BLUE\\] \"
    echo -e \$prompt"

这就是我把提示符作为函数字符串的主要原因。这个转义序列并不影响行长度,它只是一个Unicode字符。

3个回答

5
\[...\]序列表示完全忽略字符串的这一部分,当您的提示包含长度为零的序列时(比如控制序列可以改变文本颜色或标题栏),这非常有用。但是,在这种情况下,您要打印一个字符,所以它的长度不为零。也许您可以通过使用无操作转义序列来欺骗Bash计算正确的行长度来解决这个问题,但听起来这样做会导致疯狂。
正确的解决方法是让Bash在计算行长度时正确理解UTF-8(或您正在使用的任何Unicode编码)。嗯,您尝试过没有\[...\]序列吗? 编辑:以下实现了我在下面评论中提出的解决方案。保存光标位置,然后在\[...\]之外打印两个空格,然后恢复光标位置,并在两个空格上打印Unicode字符。这假设固定字体宽度,Unicode字符的宽度为双倍。
PS1='\['"`tput sc`"'\]  \['"`tput rc`"'༇ \] \$ '

至少在OSX终端中,Bash 3.2.17(1)-release版本可以通过简单的测试。

为了保持透明度和易读性,我忽略了将提示符功能放入函数以及颜色编码的要求;这只是将提示符更改为字符、空格、美元符号提示符、空格。根据您稍微复杂的需求进行调整。


我已经尝试过不使用 \[ \],但是结果仍然是乱码。然而,如果我将 ༇ 替换为普通字符,比如 x,我就看不到这个问题了(同样不使用转义序列)。我还尝试过使用 △ 作为另一个字符,但是结果相同。 - Andy Ray
你可以尝试用一对curses命令将有问题的序列包装起来,类似于tput sc、打印两个空格、tput rc、打印你的Unicode字符。这两个空格(或其他具有正确宽度的序列)必须在\[...\]之外,其余内容则在其中。另请参阅http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html。 - tripleee

4
@tripleee获胜了,我在这里发布最终解决方案,因为在评论中发布代码很麻烦:
CHAR="༇"
my_function="
    prompt=\" \\[`tput sc`\\]  \\[`tput rc`\\]\\[\$CHAR\\] \"
    echo -e \$prompt"

PS1="\$(${my_function}) \$ "

这里提到的技巧是使用命令tput sctput rc以保存并恢复光标位置。代码实际上是在保存光标位置,打印两个空格作为宽度,然后将光标位置恢复到空格之前,最后打印特殊字符,使得线的宽度来自于两个空格而不是字符本身。

嵌套变量是怎么回事呢?给“prompt”赋值只为了打印并且丢弃它似乎特别浪费。 - tripleee
我提供了一个片段来展示使用情况,这个片段来自更大的函数。整个文件现在已经可以运行了(多亏了您),底部是一个巨大的字符串:git、svn和hg信息都以漂亮的格式出现在提示符中。https://github.com/DelvarWorld/configs/blob/master/.bashrc - Andy Ray

0
(不是解决您问题的答案,而是与您问题相关的一些指针和经验。)
我经常看到您描述的关于命令行编辑(Ctrl-R,... Cntrl-A Ctrl-E ...)的行为,即使没有Unicode字符。
在一个工作场所,我花时间弄清楚了终端对TERM设置的解释与操作系统使用的TERM定义之间的差异(好吧,我想是stty)。现在,当我遇到这个问题时,我会退出当前尝试编辑该行,再次将该行提取出来,然后立即进入“vi”模式,打开vi编辑器(只需按下“v”字符,对吧?)。所有完整会话的vi易用性;为什么要少呢 ;-)?
再次查看您的问题描述,当您说
my_function="
    prompt=\" \[\$CHAR\]\"
    echo -e \$prompt"

这只是一个字符串定义,对吧?我猜你简化了问题的定义,假设这是你的my_function的输出。在创建函数定义、调用函数和使用返回值的步骤中,很可能有很多机会让shell-quoting不能按照你想要的方式工作。

如果你编辑你的问题,包括my_function的定义和完整的使用(将你的函数缩小到引起问题的部分),其他人也许更容易帮助你解决问题。最后,你经常使用set -vx吗?它可以帮助显示变量扩展的如何/何时/什么,你可能会在那里找到一些东西。

如果所有这些都失败了,请查看Orielly termcap & terminfo。你可能需要查看本地系统的stty和相关命令的man页面,并且你可能需要寻找特定于你Linux系统的用户组。

希望这能帮到你。


1
stty(1) 在这里有什么关系? - tchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接