如何在外壳中以不同的方式显示控制字符(^ C,^ D,^ [,…)


13

当您在外壳中键入控制字符时,它们将使用所谓的“插入符号”显示。例如,转义^[符用插入符号表示。

我喜欢自定义bash shell,使其看起来很酷。我已经例如改变了我PS1,并PS2成为着色。我现在希望控制字符也具有独特的外观,以使其与常规字符更具区别。

$ # Here I type CTRL-C to abort the command.
$ blahblah^C
          ^^ I want these two characters to be displayed differently

有没有办法让我的shell突出显示控制字符?

是否可以使其以粗体显示或以不同于常规文本的颜色显示?

我在这里使用bash shell,但是我没有用这个标签标记问题,bash因为也许有一种适用于许多不同shell的解决方案。

请注意,我不知道在什么级别突出显示控制字符。我首先以为它在外壳本身中。现在,我听说是readline控制bash之类的 shell中控制字符的方式。因此,该问题现在带有标记,readline而我仍在寻找答案。


您为什么认为外壳突出了它们?因为bash它正在readline处理这些东西,而对于大多数其他人来说,它是tty驱动程序。
mikeserv

我不知道 这就是为什么我写了便条。这只是我的猜测,因为其他应用程序喜欢viless经常以不同于常规文本的方式突出显示控制字符。我将使用此信息来编辑问题。谢谢!
wefwefa3

那讲得通。对于外壳来说,这种东西更难,因为它是第二优先级。对于编辑器,他们的全部工作是提供一个屏幕<>文件界面,但是shell首先是用于解释语言的命令解释器,其次是(或者根本没有)行编辑器的命令解释器。
mikeserv

Readline由其他程序使用,但它也是 bash的一部分:它是链接到其中的库。今天的贝壳,尤其是bash,几乎可以做任何您能想到的事情。
Alexis

只需使用zsh开箱即用的功能即可。
斯特凡Chazelas

Answers:


20

当您按下Ctrl+X,您的终端仿真器写入字节为0x18到伪终端对的主面。

接下来发生的情况取决于tty行规程(内核中位于主端(在仿真器的控制下)和从属端(在终端中运行的应用程序进行交互)之间的软件模块)的配置方式。

配置tty行规程stty命令是该命令。

当运行这样的愚蠢的应用程序时cat,它不知道并且不关心其stdin是否为终端,该终端处于默认规范模式,其中tty行规程实现了粗线编辑器

一些需要比粗线编辑器更多的交互式应用程序通常在启动时更改这些设置,并在离开时恢复它们。在它们的提示下,现代外壳就是此类应用程序的示例。他们实现了自己的更高级的行编辑器。

通常,当您输入命令行时,shell会将tty行规则设置为该模式,并且当您按Enter键以运行当前命令时,shell会恢复正常的tty模式(如发出提示之前的效果一样)。

如果运行stty -a命令,您将看到哑应用程序正在使用的当前设置。你可能会看到icanonechoechoctl被启用设置。

这意味着:

  • icanon:启用了粗线编辑器。
  • echo:键入(即终端仿真器写入到主侧)字符呼应背部(供终端仿真器读取提供)。
  • echoctl:而不是被的回荡 ASIS,控制字符被呼应^X

因此,假设您输入A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Return

您的终端仿真器将发送:AB\bC\x18\b\r。线路规程将回声回:AB\b \bC^X\b \b\b \b\r\n,和读取从从设备侧的输入的应用程序(/dev/pts/x)的读出AC\n

该应用程序看到的全部是AC\n,并且仅在您按下Enter该按钮时才对^X那里的输出没有任何控制。

您会注意到,对于echo,第一个^H^?带有某些终端,请参见erase设置)导致\b \b被发送回终端。这是将光标移回,用空格覆盖,再次移回光标的顺序,而第二个顺序^H导致\b \b\b \b擦除了这两个^X字符。

^X(为0x18)本身正在翻译成^X用于输出。像一样B,它没有进入应用程序,因为我们使用Backspace删除了它。

\r(aka ^M)转换为\r\n^M^J)进行回显,将\n^J)转换为应用程序。

那么,对于那些愚蠢的应用程序,我们有哪些选择:

  • 禁用echostty -echo)。通过...不回声任何东西,有效地改变了控制字符的回声方式。并不是真正的解决方案。
  • 禁用echoctl。这改变了控制字符(^H^M...和行编辑器使用的所有其他字符)的回显方式。然后按原样回显它们。例如,ESC字符作为\e^[/ 0x1b)字节^G发送\a(终端将其识别为转义序列的开始),而您发送了(BEL,使您的终端发出蜂鸣声)...不是一种选择。
  • 禁用粗线编辑器(stty -icanon)。并不是真正的选择,因为原始应用程序将变得不那么可用。
  • 编辑内核代码以更改tty行规则的行为,以便发送控制字符的回声\e[7m^X\e[m而不是发送回声^X(此处\e[7m通常在大多数终端中启用反向视频)。

一种选择可能是使用像rlwrap这样的包装器,这是一种肮脏的技巧,可以将奇特的行编辑器添加到愚蠢的应用程序中。实际上,该包装程序尝试read()将终端设备中的simple替换为对readline行编辑器的调用(这确实会更改tty行规程的模式)。

更进一步,您甚至可以尝试像这样的解决方案,解决方案^X依赖于GNU屏幕的:exec功能,劫持来自终端的所有输入以通过zsh的行编辑器(恰好在反向视频中突出显示s)。

现在,对于确实实现自己的行编辑器的应用程序,由他们来决定如何完成回显bash使用readline表示不支持自定义控制字符回显方式的内容。

对于zsh,请参见:

info --index-search='highlighting, special characters' zsh

zsh确实会默认突出显示不可打印的字符。您可以使用以下方法自定义突出显示:

zle_highlight=(special:fg=white,bg=red)

对于那些特殊字符用红色突出显示的白色。

但是,这些字符的文本表示不可自定义。

在UTF-8语言环境,为0x18将呈现为^X\u378\U7fffffff(二未分配的Unicode代码点)为<0378><7FFFFFFF>\u200b(一个不真正打印Unicode字符)的<200B>

\x80在iso8859-1语言环境中将被呈现为^�...等。


3

我通常在我的.bashrc文件中有以下代码:

function get_exit_status()
{
        local code=$?
        if [ $code -ne 0 ]
        then
                printf $'\001\033[31m\002'"($code)"$'\001\033[0m\002'" "
        fi
}

然后在我的PS1中调用此函数

PS1='\u@\h \w $(get_exit_status)'

这样,如果您按^ C,您将在提示符下看到它

I@mycomputer ~ ^C
I@mycomputer ~ (130)

将提示所有非“ 0”的退出状态代码。


$?扩展到PS1中的退出状态,例如PS1 ='$?\ u @ \ h \
w'– teknopaul
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.