这是怎么回事。

这是怎么回事。

使用 bash shell 时,我尝试使用Shift+LEFT突出显示并复制我输入的命令(而不是使用鼠标)。然而,我却得到了很多C。后来我意识到Shift+UP使AShift+DOWN使BShift+RIGHT也使D

为什么会发生这种情况?

我认为它来自原始击键数据(^[[A^[[B^[[C^[[D),但它只是一个大写字母(^[[开头没有)。

答案1

这是一个可以追溯到 20 世纪 80 年代的键盘输入协议,而你的 shell,不是正如 M. Vazquez-Abrams 的回答所示,您的“终端驱动程序”(无论应该是什么)没有正确处理它。此外,它是一个完全有效的控制序列。

背景

终端发出功能键和扩展按键的控制序列。它们可以发出DECFNK控制序列,这是CSI引入的控制序列; Linux功能键控制序列,这是CSI引入的不同类型的序列; SCO控制台功能键控制序列,是CSI引入的第三种序列;移位的单个字符,以 SS3 为前缀;或者,如本例所示,用于各种事物的 ECMA-48 标准序列。

(SS3 和 CSI 是 C1 范围内的控制字符。Single Shift 3 和控制序列引导符。)

您的(IBM M 型或类似型号)键盘上有两个特定的键盘:计算器键盘和光标键盘。 DEC VT 风格的终端仿真器(这是您可能遇到的大多数终端仿真器,从内核中的终端仿真器到 unicode-rxvt)采用的模型是两个键盘都具有可单独切换的应用程序/正常模式。全屏 TUI 应用程序、使用 libedit 或 GNU readline 库(或 ZLE)(例如 shell)的应用程序以及其他一些类型的应用程序指定它们想要的模式,然后通过读取突发来监听来自终端的控制序列字符数(因为人类无法像终端或终端仿真器发送控制序列一样快地键入完整的 ECMA-48 控制序列,因此所有突发事件都是用户按键Esc与终端的区别仿真器发送以 ␛ 字符开头的控制序列)。

  • 在应用程序模式下,每个键盘上的箭头键都会产生以 SS3 为前缀的移位单个字符。修饰符实际上不能产生任何效果(尽管 XTerm 搞砸了),因为 ECMA-35 和 ECMA-48 将 SS2 和 SS3 定义为仅作用于单个后续字符。但是,另一方面,计算器和光标键盘会生成不同的 SS3 移位字符,从而使两个键盘能够相互区分。
  • 在正常模式下,每个键盘上的箭头键都会产生相同的CSI引入的控制序列,它们来自ECMA-48通过 DEC VT 的增强。特别是,光标键发送 ECMA-48 控制序列 CUU、CUD、CUR 和 CUL(CUrsor Up、CUrsor Down、CUrsor Right 和 CUrsor Left)。 ECMA-48 控制序列的 DEC 增强是控制序列包括当前修改器状态。

因此,人们可以在应用模式和正常模式之间进行选择,在应用模式中,人们无法知道按下了哪些修饰键,但可以区分两个向左箭头键,而在正常模式中,人们无法区分两个箭头键,但可以知道按下了哪些修饰键。

更详细地说:ECMA-48 控制序列的 DEC 增强是控制序列有两个参数:

  • 根据 ECMA-48,第一个参数类似于 CUU、CUD、CUR 或 CUL 实际可以具有的第一个参数。它是出现次数,因此始终为 1。
  • 第二个参数是有趣的。它包含修饰键状态,(由于涉及 CSI 引入的控制序列中的参数在省略时如何工作的原因)是各种修饰键的一组位标志,加上 1,编码为十进制数。

这就是 DEC VT 码头自 20 世纪 80 年代以来一直在做的事情。近年来,几个终端仿真器最终引入了相同的功能(尽管,如前所述,XTerm 的做法相当错误)。

这是怎么回事。

问题在于您的 GNU readline 库、libedit、ZLE 等没有真正正确地处理协议。他们不完全是罪魁祸首。他们依赖于 termcap 和 terminfo 系统,而这些系统根本无法胜任这里的工作。 termcap 和 terminfo 实际上没有输入控制序列的概念可能会有所不同,更不用说多模式键盘了。

为此,您必须寻找 Vim 之类的工具,它可以使用 terminfo 的特殊覆盖进行编程,以指定遵循上述协议的控制序列(参见:help xterm-modifier-keysVim 中),或 NeoVIM,它使用保罗·埃文斯 (Paul Evans) 的 libtermkey和它的CSI驱动程序。 libtermkey 的 CSI 驱动程序是人们如何正确处理来自类似 DEC VT 的终端仿真器的键盘输入的方式。它是一个实际的 ECMA-48 状态机解析器,可以正确解码控制序列。

但是你的 shell 正在做的是在 terminfo 中查找箭头键的条目,并且只匹配那些特定的控制序列

具体来说:

  • 您的 shell 正在其 terminfo 记录中查找kcub1终端的功能。这是来自记录teken, 例如:
    % tput -T teken kcub1|hexdump -C
    00000000 1b 5b 44 |.[D|
    00000003
    %
  • 这是仅有的将该特定输入序列匹配为← Left Arrow
  • 当您按⇧ Level 2 Shift+时,← Left Arrow您的终端仿真器正在发送控制序列 CSI 1 ; 2 D。相反,它使用 7 位替代方案并发送 [ 1 ; 2 D,其中 [是以 7 位字符编码 CSI 的方式。
  • 您的 shell 无法将其与 terminfo 中的任何已知固定输入序列进行匹配,并中止处理。在我的 Bourne Again shell 中,它最终会吞掉前两个字符,并且表现得就像我按下了 一样; 2 D。在你的 Bourne Again shell 上,它最终会吞掉前四个字符,并且就像你按下了 一样D

    故障模式是什么取决于它尝试模式匹配的输入序列的确切集合,因为这决定了在确定它具有没有可能匹配的序列之前它吞咽了多少个字符。当然,这又取决于终端的 terminfo/termcap 记录实际包含的内容以及您告诉 shell 终端的终端类型。

修复

解决此类问题的本地解决方法是对 shell 中的键绑定发挥创意。例如,这就是为什么您会发现人们在 s 中使用 Z shell 做这种事情.zshrc

bindkey "\e[1;5D" 向后字
bindkey "\e[1;5C" 前向字

不幸的是,没有非本地修复。这将涉及到重新构建 shell 的输入处理。这种重新架构早就该进行了。 (NeoVIM 就是见证。)但目前还没有人解决这个问题。

进一步阅读

答案2

Ctrl+V将导致下一个按键按字面输入。对于Shift+,结果为“^[[1;2A”。终端驱动程序将“^[[1;2”用作无效转义序列,只留下“A”。

相关内容