Vim 修炼场
CHAPTER 09 · 进阶篇 下

寄存器与剪贴板:复制为什么会被删除顶掉

预计阅读 25 分钟(含练习约 1 小时)· 前置:第 5 章 · 本章演示 5 个

学习目标

  • 解开 Vim 最著名的「bug」:yy 复制 → dd 删除 → p 粘出来的是被删的行,并练成 "0p 肌肉自救
  • 建立寄存器 = 一排抽屉的心智模型:无名、数字、命名、只读、黑洞、系统剪贴板,各类的「谁写它、拿来干嘛」一张表认清
  • 掌握命名寄存器 "a-"z 与大写追加("A)—— 收集散落内容、为第 10 章宏打前站
  • 学会黑洞寄存器 "_:删除不污染,「复制保真」的第二条路
  • 打通系统剪贴板 "+:vim 与微信/浏览器/IDE 之间自由搬运,并了解两边的自动打通配置
  • 会用 :reg 查看寄存器内容,丢失前先看清单

1. 一个「bug」引出的一章

每个 Vim 用户都撞过这堵墙:用 yy 复制了一行,顺手 dd 删掉一行不要的,然后 p 想粘贴 —— 粘出来的是刚删掉的那行,复制的东西像被偷走了。第一次遇到的人几乎都以为是 bug,或者以为是自己按错了,重试一遍,还是删掉的行。

这不是 bug,是 Vim 剪贴板模型的必然结果,看懂它就看懂了整章:Vim 的默认粘贴源叫「无名寄存器」,它只有一个坑,而 y(复制)和 d(删除)都往这个坑里写。你复制了 title,又删了 note —— 坑里现在是 note,因为后来的写把先来的顶掉了。Vim 这样设计有自己的道理(删除即「剪切」,随手粘贴刚删的内容本来就是高频操作),但代价就是「复制」不设防。

值得花一分钟体会这个设计的来龙去脉。Vim 的世界里「删掉一段、马上贴到别处」(挪动)是重构里最高频的动作,于是删除顺理成章地承担了「剪切」的职责 —— 默认不设防换来的是挪动零成本。而真正的「复制后待用」,Vim 给了专门保险柜("0)和一排手动抽屉("a-"z):它不是没有解决方案,而是把「默认通道留给最高频的动作」。理解了这层取舍,你就不会再纠结「为什么默认这么坑」,而是记住本章的主旋律:默认通道跑高频,重要内容走保险。

解药在演示的结尾:"0p —— 从 0 号寄存器取。0 号寄存器是 yank 的专属保险柜:每次 y 复制,内容同时写入无名寄存器和 "0;而 d 只写无名寄存器,碰不到 "0。所以「复制完删了点东西,现在要粘贴刚才复制的」的完整答案永远是 "0p。这一章的任务就是把这个坑认识全,顺便打开它的兄弟姐妹 —— 一共 48 个左右。

2. 心智模型:一排抽屉,各有分工

寄存器(register)就是 Vim 内部的多格剪贴板。普通编辑器只有一格(系统剪贴板),复制新内容顶掉旧内容;Vim 有一排抽屉,每格有名字、有分工、有写入规则。总表先认脸,后面逐个开柜:

类别名字谁写它拿来干嘛
无名寄存器""y d c s x —— 所有复制删除默认都写它默认粘贴源;一个坑,后来者顶掉先来者
yank 保险柜"0只有 y 写它(与 "" 同步各存一份)"0p 复制保真的标准答案
数字删除梯"1–"9整行/多行删除依次入 "1,旧内容向后顺延后悔药:取回前几刀删掉的内容
行内小件"-不足一行的删除(x dw 等)小件删除也有存档,很少手动取
命名抽屉"a–"z只有你显式写("ayy)手动行李箱:存住重要内容随便折腾;大写追加
只读三件". ": "%Vim 自动维护最近插入文本 / 最近命令 / 当前文件名
黑洞"_只有你显式写("_d)纯删除不留档 —— 保护无名寄存器不被污染
系统剪贴板"+ "*你显式写,或与系统共享与 IDE/浏览器/微信 互通

读表的正确姿势不是背,是回答三个问题:①谁有权写(默认写?只有 y 写?只有我写?谁都不写?)②会不会被顶掉③什么时候值得指名取。日常 95% 的场景只用三格:""(默认)、"0(保真粘贴)、"+(对外搬运);但「命名抽屉 + 黑洞」这两个主动技能,用会上瘾。

用一个重构下午的场景把表激活:你正在改一个函数 —— yy 复制了签名准备参考,顺手 dd 删了三行废代码("" 被顶了三次,但 "0 里签名还在);又想把函数里某个待办事项挪到周报里,"ayy 存进 a 抽屉;继续删删改改(传送带 "1-"9 记着你的删除史);最后 "0p 贴出签名比对,"+yy 把结论发进聊天工具。整个下午,你其实一直在寄存器之间调度 —— 只是以前不知道,现在知道了,调度就变成了主动的。

3. 无名寄存器 "":默认的公共坑

先把这个最熟的坑说透。d c s x y 全部默认写 ""(两个双引号)—— 这意味着 Vim 里「删除」其实全是「剪切」。这个设计的好处真实存在:改代码时大量操作是「把这段挪走」,删完立刻 p 贴到别处,零额外按键。坏处你也见识了:它对「复制」不设防。所以给一条使用纪律:

纪律:复制了打算待会儿用的东西,中间又必须删内容时 —— 要么粘贴前想一句「粘谁」,要么删除时就地隔离(黑洞,§6)。注意 "" 里按行/字符分开粘贴行为:整行复制粘贴自动落在上/下方(第 7 章 V 的伏笔),字符复制粘贴从光标后接上 —— 这个形状语义第 5 章讲过,寄存器只是仓库,形状跟着内容走。顺带复习 P(大写):粘到光标前/当前行上 —— p 与 P 的选择,看你想让新内容落在锚点的前面还是后面;交换两字符的 xp、交换两行的 ddP,都是这个形状语义的应用(第 5 章的老朋友)。

4. "0 与数字梯 "1-"9:保险柜与传送带

两个「自动保险」系统。0 号是 yank 保险柜(§1 已讲),数字梯是删除史传送带:

规则一句话:每次行级删除,新内容进 "1,原 "1–"8 依次后移一位("9 掉出传送带消失)。所以 "1p 永远是「最近一刀」,"2p 是倒数第二刀 —— 演示里三连删之后用 "2p 取回了中间那刀。不足一行的删除(x、dw)不占传送带,进 "- 小件柜。:reg 随时打开全柜清单 —— 「刚才删的东西还能找回来吗?」这个问题,先 :reg 再回答。

两个「保险柜」的分工容易混,记一组对照就清:"0 是复制的保险柜,"1 是删除的最近档 —— y 永远不进数字梯,d 永远不碰 "0。所以「粘我刚复制的」→ "0p;「粘我上一刀删的」→ "1p(其实直接 p 就是它,"1p 的价值在于连删几刀后取历史)。一个数字冷知识:行级删除只有不指定寄存器时才走传送带,"add 删进 a 抽屉的内容不会占据 "1 —— 指定抽屉的操作完全绕开自动系统,这就是 Vim 寄存器「显式优先于隐式」的统一哲学。

5. 命名寄存器 "a-"z:你的手动行李箱

26 个抽屉 "a–"z,规则极简:只有你显式指名时才读写,其他任何操作不碰它们。语法是「前缀 " + 名字 + 动词」:"ayy 复制进 a、"bdd 删除进 b、"ap 从 a 取。它的价值在于「存住,随便折腾」:

演示里的「收集散落各行」是经典用法:写周报挑重点行、重构时归拢同主题代码。大写字母是追加开关:"Ayy 往 a 抽屉追加而不是覆盖 —— 抽屉变成购物车。这里埋一颗重要的种子:第 10 章的宏就存在命名寄存器里(qa 录制 = 往 a 写按键序列,@a 执行 = 从 a 读),所以「前缀 + 名字」的手感现在练熟,宏那章直接白捡。

抽屉的使用习惯也值得立两条:①一屉一用 —— 给 a 固定安排「当前任务收集」、给 b 安排「常用代码片段」,位置稳定,肌肉记忆才有意义;终端 Vim 里抽屉内容关掉重开也还在(shada/viminfo 负责持久化),但 IDE 插件对寄存器的跨会话保留行为不一 —— 重要内容别指望隔夜,当天用完当天清:塞满杂物的抽屉等于没有抽屉,定期用 "ayy 覆盖重置即可。②追加注意形状 —— 行级追加行、字符追加字符,混着追加会在粘贴时得到奇怪的拼接,收集场景统一用行级(yy/dd)最稳。

6. 黑洞 "_ 与只读三件

黑洞寄存器 "_ 解决「删除污染」的另一头:把删除的内容扔进一个谁也读不到的地方 —— 纯删除,不留档、不顶任何抽屉:

使用场景一句话概括:刚复制了东西,接下来要删的内容都是垃圾 —— "_dd、"_diw 批量清理,清完 p 依然粘出你的宝贝。它和 "0p 是同一问题的两个解法:前者在删除时隔离,后者在粘贴时取保险柜 —— 黑洞更从容(不用记粘贴时换键),"0p 更普适(忘了隔离也能救)。都要会。

只读三件顺路认识:". 存最近一次插入模式敲的文本(想再贴一遍刚打的一段字,插入模式里 Ctrl+r.);": 存最近一条命令;"% 存当前文件名(写 :e %:h 之类命令时有用)。它们只读,写不进去,当系统提供的三个小便利贴即可。

7. 系统剪贴板 "+ 与 "*:对外口岸

Vim 世界与系统剪贴板之间有两个口岸:"+(selection clipboard 的「主口岸」)和 "*。macOS 上两者等价;Linux 图形环境下 "* 是「鼠标选中即复制」的 primary selection,"+ 才是传统剪贴板 —— 跨平台开发时知道这个区别能省很多困惑。用法照旧是前缀语法:

在 IDE 里这条通道尤其宽松:VSCodeVim 和 IdeaVim 都可以配置成默认互通(y/p 直接走系统剪贴板,配置见本章 IDE 备注)。互通有互通的代价:你的 vim 复制会顶掉系统剪贴板里网页复制的内容 —— 两条通道混成了一条。个人建议:默认不互通,需要对外时显式 "+ —— 按键多两下,换「vim 内部流转」与「对外搬运」互不干扰。当然高频对外搬运的人反向选择也合理,这是口味题,不是对错题。

展开说一句「双通道」哲学:不互通的配置下,你其实握着两条独立的剪贴板 —— vim 寄存器集群跑内部流转(改代码、挪内容,快进快出),系统剪贴板跑对外交互(微信、浏览器、issue)。两条通道各干各的,谁也不顶谁:从网页复制一段代码,随手在 vim 里 y 了别的东西,再 "+p —— 网页那段还在。互通配置则把两条通道焊死成一条,少按两键,但「刚才网页复制的被我的 y 顶了」这类事故也从此常驻。想清楚自己哪类操作更频繁,选择自然就有了。

8. :reg 与两个小入口

:reg(registers)打开全部抽屉的清单:每个名字 + 内容摘要,按回车关闭。丢东西了先看它;想确认 "a 里存的什么也看它。两个顺手的补充:①清单里只显示有内容的抽屉,空的不占屏;②:reg a 只看指定的几个(:reg a b 0)。取内容的入口不止 p:插入模式里 Ctrl+r + 名字,把抽屉内容「打」进当前文本(比如 Ctrl+r% 插入文件名)—— 写文档、拼命令时极顺手。

9. 决策表:复制粘贴的完整攻略

把全章收敛成一张「遇到什么,按什么」:

场景标准动作原理
复制了、没删过东西、现在粘贴p无名寄存器还是复制的内容
复制后删过东西,要粘【刚删的】(挪动)p删除=剪切,本来就是它
复制后删过东西,要粘【之前复制的】"0p0 号是 yank 保险柜
要删垃圾,但待会儿还要粘贴刚复制的"_d…(黑洞删)删除不顶无名寄存器
存几样东西,中间大量复制删除"ay… 存,"ap 取命名抽屉不被动
收集散落的多行"Ayy 逐行追加大写=追加模式
找回收删掉的东西:reg 查看,"1p/"2p 取数字梯存最近 9 刀
与浏览器/微信/同事传代码"+y… / "+p系统剪贴板口岸

10. 每日训练计划(30 分钟 × 2 天 + 复习)

天时间盒内容通过标准
第 1 天0–10 分钟重放演示 1、5,读 §1–§4;靶场复现陷阱 5 遍(yy→dd→p→u→"0p)"0p 不假思索
10–30 分钟练习 1 + 练习 3黑洞与 "0p 两条路都走得顺
第 2 天0–10 分钟重放演示 2、4,读 §5–§8;"ayy/"Ayy/"ap 三连打 10 组前缀语法不看键盘
10–30 分钟练习 2 + 练习 4盲测 10 题对 7 题,本章毕业

11. 如果本章只记住三件事

  1. 复制会被删除顶掉,不是 bug:无名寄存器一个坑,y 和 d 都写 —— 粘「之前复制的」用 "0p(yank 保险柜);
  2. 主动技能两个:命名抽屉 "a… 存住随便折腾(大写追加,宏的预备课),黑洞 "_d… 删除不污染;
  3. 对外走 "+,对内走默认:系统剪贴板是口岸不是主场;丢了东西先 :reg —— 数字梯里存着最近 9 刀。

内容能存取了,「重复劳动」还差最后一块拼图:第 10 章的撤销、. 重复与宏 —— 而宏,就寄存在你刚练熟的命名抽屉里。

练习

练习 1:陷阱复现与自救(10 分钟)

靶场 5 组,每组:①yy 复制一行 ②j 到下一行 dd 删除 ③p 观察粘出的是删掉的行 ④u 撤销 ⑤"0p 粘出真正复制的行 ⑥两次 u 回到原状。通过线:第③步能提前说出会粘出什么,第⑤步不需要回想按键。

练习 2:行李箱收集(10 分钟)

一段 ≥15 行代码,任务是收集其中 3 行散落的重点行:①"ayy 存第一行;②中间做 3 次无关的 y/d 操作(污染无名寄存器);③"Ayy 追加第二行、再去第三行同样追加;④文件末尾 o 开新行 Esc,"ap 取出三行。验证:取出的顺序与收集顺序一致,中间的污染操作没有影响任何内容。

练习 3:黑洞隔离对照实验(10 分钟)

两遍对照:A 遍:yiw 复制一个词 → 到垃圾行 dd → p,亲眼看粘出垃圾行 → "0p 才是对的。B 遍:同样 yiw → "_dd 黑洞删 → p 直接粘出正确的词。各 5 遍。通过线:两种场景(先污染后救 / 一开始就隔离)都不用想超过一秒。

练习 4:盲测毕业考(10 分钟)

10 题先说答案再看:①yy 后 dd,p 粘出谁的内容?②粘「之前 yank 的」怎么按?③把当前行复制进 a 抽屉 ④把当前行【追加】进 a 抽屉 ⑤从 a 取出粘贴 ⑥删除但不污染无名寄存器 ⑦复制当前行到系统剪贴板 ⑧查看所有寄存器 ⑨最近一次删除存在哪个寄存器?⑩倒数第二刀删除怎么取回?答案:dd 的 / "0p / "ayy / "Ayy / "ap / "_dd(或 "_dw 等) / "+yy / :reg / "1 / "2p。10 对 7 毕业。

常见陷阱

🕳 复制被删除顶掉,以为 Vim 有 bug 这是本章整章的由来,也是新手流失点第一名。三条纪律任选其一养成:①复制后要粘贴前,中间的删除一律 "_ 黑洞化;②粘贴前默想「无名里现在是谁」,复制的内容用 "0p 兜底;③重要内容一开始就 "a 存抽屉。三条殊途同归:别裸奔着穿过「复制-删除-粘贴」的雷区。
🕳 连删几刀后,"1p 出来的不是想要的那刀 数字寄存器是传送带:每删一刀,所有格子向后顺延 —— "1 永远是最近一刀,你惦记的那刀可能已经在 "2 "3 了。别猜,:reg 打开清单看一眼,指名取。另外注意:不足一行的删除(x dw)不占传送带,别在数字梯里找它们(在 "-)。
🕳 "* 与 "+ 分不清(跨平台坑) macOS 上两个都指向系统剪贴板,随便用;Linux X11 下 "* 是「鼠标划选即复制」的 primary selection,"+ 才是 Ctrl-c/Ctrl-v 的剪贴板 —— 远程开发、容器、WSL 里行为还会再变。经验法则:写教程、写脚本统一用 "+,行为最一致;IDE 里则建议直接用 IDE 的 Cmd-c/Cmd-v(见下条)。
🕳 开了「默认互通」,内部流转反而乱了 配置 y/p 直通系统剪贴板(vim.useSystemClipboard / set clipboard+=unnamed)很方便,但代价:①vim 里每次复制都会顶掉系统剪贴板(刚从网页 Cmd-c 的东西没了);②yank-then-delete 陷阱的战场从 vim 内部搬到系统层,一样存在。高频对外选互通,高频内部选隔离 + 显式 "+ —— 两种都试一周再定,别只看教程默认值。

VSCode / IDEA 差异

VSCode(VSCodeVim)

  • 寄存器系统 VSCodeVim 完整实现:"0p、"a…、"_…、:reg 全部照本章用
  • 系统剪贴板:①显式通道 "+y/"+p 可用;②可视模式里 Cmd+c/Cmd+v 直通系统;③设置 "vim.useSystemClipboard": true 让 y/p 默认互通(取舍见陷阱四)
  • VSCode 原生的复制粘贴(Cmd+c/v,插入模式或无模式选中时)与 vim 寄存器是两套体系,互不干扰 —— 这其实是天然的「内部/对外」双通道

IntelliJ IDEA(IdeaVim)

  • 寄存器同样完整支持;:reg 清单由 IdeaVim 自绘,内容与 IDEA 的剪贴板历史(⌘⇧V 粘贴历史弹窗)分开存
  • 系统剪贴板:显式 "+ 可用;自动互通写 .ideavimrc:set clipboard+=unnamed(推荐放第 12 章的配置全文里一起定夺)
  • IDEA 特有福利:Cmd+Shift+V 打开粘贴历史(最近 N 条系统复制),相当于把系统剪贴板也做成了一排抽屉 —— 与 vim 数字梯互补

本章速记

按键 / 命令作用按键 / 命令作用
""(无名)默认粘贴源,y/d 都写(一个坑)"0p取 yank 保险柜 —— 复制保真
"1–"9删除传送带(最近 9 刀)"-行内小件删除存档
"ayy / "ap存入 a 抽屉 / 从 a 取"A…大写 = 追加不覆盖
"_d…黑洞:纯删除不污染"+y… / "+p系统剪贴板进出
:reg查看全部抽屉清单". ": "%只读:最近插入/命令/文件名

一句话心法:复制会被删除顶掉("0p 保真),要主动就用抽屉("a 存 "A 追)与黑洞("_ 删),对外走 "+,丢了先 :reg。