如何在 shell 中以不同方式显示控制字符(^C、^D、^[、...)

如何在 shell 中以不同方式显示控制字符(^C、^D、^[、...)

当您在 shell 中键入控制字符时,它们会使用所谓的“插入符号”显示。例如,转义符被写成^[脱字符号。

我喜欢定制我的 bash shell,让它看起来很酷。例如,我改变了我的PS1PS2变得彩色。我现在希望控制角色也能获得独特的外观,以使它们与常规角色更容易区分。

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

有没有办法让我的 shell 以不同的方式突出显示控制字符?

是否可以使其以粗体字体显示,或者使它们以与常规文本不同的颜色显示?

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

笔记我不知道控制字符的突出显示发生在什么级别。我首先以为它在壳本身中。现在我听说它是阅读线控制控制字符在 shell 中的显示方式,例如巴什。所以这个问题现在被标记为readline,我仍在寻找答案。

答案1

当您按 时Ctrl+X,终端仿真器会将字节 0x18 写入伪终端对的主端。

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

一个命令来配置它tty线路纪律stty命令。

当运行这样一个愚蠢的应用程序时,cat它不知道也不关心它的标准输入是否是终端,终端处于默认状态典范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。线路纪律将回声back: AB\b \bC^X\b \b\b \b\r\n,从从机端 ( ) 读取输入的应用程序/dev/pts/x将读取AC\n.

所有应用程序看到的是AC\n, 并且仅当您按下时Enter,因此它无法对^X那里的输出进行任何控制。

你会注意到对于回声,第一个^H^?对于某些终端,请参阅erase设置)导致被\b \b发送回终端。这是将光标向后移动、用空格覆盖、再次将光标向后移动的序列,而第二个^H结果是\b \b\b \b删除这两个^X字符。

(0x18)本身^X被转换为^输出X。就像 一样B,它没有进入应用程序,因为我们用 Backspace 删除了它。

\r(aka ^M) 被翻译为\r\n( ^M^J) 表示回显,\n( ^J) 表示应用程序。

那么,我们对这些人有什么选择哑的应用:

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

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

更进一步,您甚至可以尝试类似的解决方案这个依赖于 GNU screen 的功能,劫持来自终端的所有输入以通过 zsh 的行编辑器(恰好^X以反白方式突出显示 s) 。:exec

现在,对于实现自己的行编辑器的应用程序,由它们来决定如何回声已经完成了。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 代码点) as <0378>, <7FFFFFFF>, \u200b(不可打印的 unicode 字符) as <200B>

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

答案2

我通常在 .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”的退出状态代码都会被提示。

相关内容