bash:Shift+箭头键使 A、B、C、D

Zac*_*ary 8 bash arrow-keys

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

为什么会发生这种情况?

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

Jde*_*eBP 11

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

背景

终端发出功能键和扩展按键的控制序列。它们可以发出 DECFNK 控制序列,它们是 CSI 引入的控制序列;Linux功能键控制序列,是一种不同的CSI引入序列;SCO控制台功能键控制序列,是CSI引入的第三种序列;移动的单个字符,以 SS3 为前缀;或者,在这种情况下,各种事物的 ECMA-48 标准序列。

(SS3 和 CSI 是控制字符,在 C1 范围内。Single Shift 3 和 Control Sequence Introducer。)

您的(IBM Model 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 终端自 1980 年代以来一直在做的事情。近年来,几个终端模拟器终于引入了相同的功能(尽管如前所述,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 正在kcub1其 terminfo 记录中查找您的终端的功能。这记录中的一个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 中使用键绑定来发挥创意。这就是为什么,例如,你会发现人们在他们的.zshrcs 中使用 Z shell 做这种事情:

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

不幸的是,没有非本地修复。这将涉及相当显着地重新构建您的 shell 的输入处理。这种重新架构早就应该进行了。(见证 NeoVIM。)但还没有人解决它。

进一步阅读


Ign*_*ams 3

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

\n