Mkdir700's Note

Mkdir700's Note

在 zsh 里同时用上 Vim 和 Emacs 快捷键

11
2025-08-21

很多开发者在终端里用 zsh 的时候,会习惯性地打开 bindkey -v,进入 vi 模式。这样做的好处很明显:命令行也能像 Vim 一样分成 普通模式插入模式,用 h/j/k/l 移动光标,w/b 按词跳转,甚至可以 dd 删除一整行,yy/p 来复制粘贴——对于重度 Vim 用户来说非常顺手。

问题是,一旦切到 vi 模式,zsh 默认的 Emacs-style 快捷键Ctrl-A 跳到行首、Ctrl-E 到行尾、Ctrl-U 清空整行等等)就全都失效了。
这些快捷键虽然简单,却是很多人输入命令时的肌肉记忆,一下子丢掉会非常别扭。

好在 zsh 的键绑定系统很灵活,我们完全可以把常用的 Emacs-style 操作重新绑定回 vi 插入模式,于是就有了一个“混合流派”:
在普通模式下享受 Vim 的强大编辑能力,在插入模式下继续用熟悉的 Emacs-style 快捷键。


怎么做

zsh 把快捷键分成不同的 keymap

  • vicmd —— 普通模式
  • viins —— 插入模式

我们只要在 viins 里加上自己想要的绑定即可。

~/.zshrc 里加上:

# 启用 vi 模式
bindkey -v
export KEYTIMEOUT=1   # 缩短 ESC 切回普通模式的延迟

# 保留常用 Emacs-style 快捷键(插入模式)
bindkey -M viins '^A' beginning-of-line   # Ctrl-A 跳到行首
bindkey -M viins '^E' end-of-line         # Ctrl-E 跳到行尾
bindkey -M viins '^B' backward-char       # Ctrl-B 光标左移
bindkey -M viins '^F' forward-char        # Ctrl-F 光标右移
bindkey -M viins '^U' kill-whole-line     # Ctrl-U 清空整行
bindkey -M viins '^K' kill-line           # Ctrl-K 删除到行尾
bindkey -M viins '^W' backward-kill-word  # Ctrl-W 删除前一个单词
bindkey -M viins '^Y' yank                # Ctrl-Y 粘贴上一次删除
bindkey -M viins '^_' undo                # Ctrl-_ 撤销(有时要 Ctrl-Shift-- 才能触发)
bindkey -M viins '^R' history-incremental-search-backward  # Ctrl-R 搜索历史

同时修复一些常见问题:

# Backspace / Delete 兼容
bindkey -M viins '^?' backward-delete-char
bindkey -M viins '^H' backward-delete-char
bindkey -M viins '^[[3~' delete-char

# 方向键和 Home/End
bindkey -M viins '^[[A' up-line-or-history
bindkey -M viins '^[[B' down-line-or-history
bindkey -M viins '^[[C' forward-char
bindkey -M viins '^[[D' backward-char
bindkey -M viins '^[[H' beginning-of-line
bindkey -M viins '^[[F' end-of-line

这样一来,插入模式下就能继续用熟悉的 Emacs-style 快捷键,而普通模式下依然保留 Vim 的强大命令。

我的配置:https://gist.github.com/mkdir700/3c4482f03009e6c30b5ba29ffa82d2a5


实际体验

配置好之后,你会得到一种非常自然的体验:

  • 普通模式
    可以用 0/^/$ 精确跳转,dw 删除一个单词,yy/p 复制粘贴,dd 删除整行……完全是 Vim 的手感。
  • 插入模式
    又能用回 Ctrl-A/ECtrl-U/KCtrl-B/F 这些简洁直接的操作,不需要刻意去记新的命令。

两套习惯不冲突,反而互补。简单的场景下用 Emacs-style,复杂编辑直接切到普通模式,一点也不违和。


调试小技巧

  • 确认某个按键序列

    cat -v
    

    按下有问题的按键,就能看到它发出的序列(例如 Backspace 可能是 ^?^H),再对应加到 bindkey 就行。

  • 查看当前绑定

    bindkey -M viins
    

总结

zsh 的 vi 模式让命令行具备了 Vim 的强大编辑能力,但默认会牺牲掉一些常用的 Emacs-style 快捷键。
通过在 viins keymap 里补回这些绑定,我们可以兼得两派的优势:普通模式下用 Vim,插入模式下保留 Emacs-style

如果你是一个习惯了 Vim,又不想放弃 Ctrl-A/E/U/K 这些经典操作的人,这套混合配置绝对值得一试。