在 bash vi 模式下,映射 jk 退出插入模式

mbi*_*ras 15 bash keyboard-shortcuts vi

我正在使用带有 bash shell 的全新安装的 ubuntu 16.04。我想做两件事:

  1. 设置 vi 模式,以便我可以从终端进行类似 vim 的运动
  2. 通过键入退出插入模式 jk

我在另一篇文章中读到如何做到这一点zsh,我该怎么做bash

tl;博士

bind '"jk":vi-movement-mode'到您的.bashrc文件后,set -o vi:)

server@thinkpad:~$ tail -n 2 .bashrc
set -o vi
bind '"jk":vi-movement-mode'
Run Code Online (Sandbox Code Playgroud)

请参阅@grochmal 的回答以获得更详细的解释

gro*_*mal 15

TL; 博士

Bash 具有与zsh's bindkeythrough类似的功能bind,但它没有vizsh. 之后set -o vi你可以:

bind '"jk":vi-movement-mode'
Run Code Online (Sandbox Code Playgroud)

这相当于zsh'sbindkey -M <all vi modes> jk vi-movement-mode

这些vi-movement-mode函数来自inputrc/etc/inputrc有关它们的列表,请参阅)。

全文

正如斯蒂芬哈里斯在他的评论中指出的那样:

  • .bashrcbashalways调用(特别是不被其他 shell调用)。

  • .bash_profile 仅在登录外壳上调用(再次,仅 bash)。

几个发行版带有一个.bash_profile如下所示的骨架:

# ~/.bash_profile
[[ -f ~/.bashrc ]] && . ~/.bashrc
Run Code Online (Sandbox Code Playgroud)

这是一个很好的内容,.bash_profile因为您可以简单地忘记它的存在。

现在,要映射jkEscshell 会话中,这是不可能的。当你这样做时:

inoremap jk <esc>
Run Code Online (Sandbox Code Playgroud)

在 Vim 中,在您键入 之后j,Vim 知道它需要稍等片刻,看看您是否键入knext 并且它应该调用映射(或者您键入另一个键并且不应该触发映射)。作为附录,这由:set timeoutlen=<miliseconds>Vim控制(请参阅 参考资料:h timeoutlen)。

一些 shell 或 X11 没有这样的超时控制并且不允许多个字符映射。只允许映射单个键(但请参阅下面的支持说明。)。

set -o vi
Run Code Online (Sandbox Code Playgroud)

不读.vimrc,它只模仿一些vi(甚至不是vim)可以在 shell 中使用的组合键。可以说是一样的-o emacs,它并没有带着全部的力量而来emacs


zsh 支持

zsh实际上支持地图超时。您可以使用以下内容映射jk<esc>

bindkey -v  # instead of set -o vi
bindkey -e jk \\e
Run Code Online (Sandbox Code Playgroud)

(那将需要去~/.zshrc~/.bashrc

然而,我建议不要这样做。我使用vimzsh大部分时间。我inoremap jk <esc>在我vimrc和我确实尝试使用bindkey上面的组合。 zsh使用时等待打印太久j,这让我很恼火。


bash 支持

bash支持readline bind. 我相信bash可以在没有的情况下进行编译,readilne因此可能会有一些罕见的系统不支持bind(注意)bash 。要映射jk<esc>bash你需要做的:

set -o vi
bind '"jk":"\e"'
Run Code Online (Sandbox Code Playgroud)

(是的,这是双重引用,这是必需的)

同样,这使得打字j很烦人。但不知何故比zsh我机器上的解决方案更烦人(可能默认超时更短)。


解决方法(对于非 bash 和非 zsh shell)

重新映射Esc键的原因是它离键盘很远,打字需要时间。可以从这些emacs人那里借用的一个技巧是重新映射,CapsLock因为无论如何它都是一个无用的密钥。 emacs伙计们Ctrl将其重新映射到,但我们会将其重新映射到Esc.

让我们xev -event keyboard用来检查 的键码CapsLock

KeyPress event, serial 25, synthetic NO, window 0x1c00001,
    root 0x496, subw 0x0, time 8609026, (764,557), root:(765,576),
    state 0x0, keycode 66 (keysym 0xffe5, Caps_Lock), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False
Run Code Online (Sandbox Code Playgroud)

并检查以下功能Esc

KeyPress event, serial 25, synthetic NO, window 0x1c00001,
    root 0x496, subw 0x0, time 9488531, (571,525), root:(572,544),
    state 0x0, keycode 9 (keysym 0xff1b, Escape), same_screen YES,
    XLookupString gives 1 bytes: (1b) "
    XmbLookupString gives 1 bytes: (1b) "
    XFilterEvent returns: False
Run Code Online (Sandbox Code Playgroud)

很好,CapsLock是键码 66,它Esc的功能叫做“Escape”。现在我们可以这样做:

# diable caps lock
xmodmap -e "remove lock = Caps_Lock"
# make an Esc key from the keycode 66
xmodmap -e "keycode 66 = Escape"
Run Code Online (Sandbox Code Playgroud)

以上必须按此顺序进行。现在每次你点击CapsLock它都像一把Esc钥匙。


棘手的部分是在哪里设置它。~/.Xmodmap包含以下内容的文件:

remove lock = Caps_Lock
keycode 66 = Escape
Run Code Online (Sandbox Code Playgroud)

大多数发行版都应该尊重(实际上是显示管理器,但为了简单起见,我说的是发行版),但我看到了不尊重多个~/X*文件的发行版。对于此类发行版,您可以尝试以下操作:

if [ "x" != "x$DISPLAY" ]; then
    xmodmap -e "remove lock = Caps_Lock"
    xmodmap -e "keycode 66 = Escape"
fi
Run Code Online (Sandbox Code Playgroud)

在您的.bashrc.

(理论上这会更好,~/.xinitrc但如果显示管理器不尊重.Xmodmap它肯定不会尊重~/.xnintrc。)

附加说明:本仅重映射CapsLockEsc一个X11会话,因此地图只能在终端仿真器的工作。实际tty的不会看到地图。

参考资料和额外阅读:

  • @Jason - 嘿,这是 Vi SE,在这里每个人都写了一个全大写的单词:“type word”&lt;Esc&gt;viwU :)。无需完成 (2认同)