身边不少同事入坑 Vim 的第一天,就被“怎么保存退出”这种基础操作难住了。我在自己的主力编辑环境里用 Vim 已经快十年,从最开始只会i、Esc、:wq三件套,到后来用宏、寄存器、多缓冲区配合完成各种重复性文本批量处理,中间踩过的坑比写过的代码还多。这篇速查笔记不是那种从安装到精通的大部头,而是我平时干活时真正高频使用、值得印在脑子里的操作合集,覆盖移动、编辑、保存退出、宏录制、递增序列、多文件切换以及 Windows 下的环境搭建,适合刚接触 Vim 的新手,也适合想把手速再提一档的老用户。
1. 先搞懂 Vim 的操作逻辑,再背命令才有意义
很多新手学 Vim 第一个反应就是“命令太多了,根本记不住”。其实 Vim 的设计逻辑非常统一,一旦你把它的底层思维理解到位,命令表只需要背一小部分,剩下的全靠规律推导。
1.1 模式化编辑为什么是 Vim 的根
平时用的记事本、VS Code、Xcode 这类工具,你敲什么字母就输入什么字符。但 Vim 不一样,它是一个“模式化编辑器”,默认情况下你打开文件之后,键盘上的按键不是输入内容,而是发命令。
我经常用开车来打比方:普通编辑器就像手动挡挂一档一直走,Vim 则是根据路况随时换挡。默认的normal模式是“命令挡”,按i之后进入insert模式才是“输入挡”,按Esc又回到命令挡。你不需要每次都想着自己现在在哪一挡,只需要记住一个核心原则:想输入文字就按i,想操作文件就回到普通模式按Esc。
很多人练 Vim 练不下去,是因为习惯性地把时间花在操作方向键和鼠标上。如果你能强迫自己两周不用方向键和鼠标,只在hjkl、w、b、e这几个移动键之间切换,肌肉记忆形成之后,你才会真正感受到“命令挡”的威力:手指不离主键盘区,眼睛不离内容区,操作的阻力会明显降低。
1.2 命令的“语法”:动词 + 范围 + 动作
Vim 的命令看起来很多,其实语法就一套。大多数普通模式命令都可以按“动词 + 范围 + 动作”来拆解,就像英语造句一样。
举几个例子:
dw= 删除到下一个单词开头。拆开看,d是“删除”这个动词,w是“跳到下一个单词开头”这个动作。diw= 删除光标所在的整个单词。这里多了一个i(inner),意思是不管光标在这个单词的哪个位置,都以单词为边界删除。d$= 删除到行尾,等价于大写的D。ci"= 修改双引号里的所有内容。c是“修改”动词,i"表示“在双引号内部”。
从这套语法往后推,你就不需要背da(、yi[、ca{之类的奇怪组合了,只要知道d/c/y(删除/修改/复制)这几个动词,再搭配i/a(内部/包含)和范围动作w、$、t、f,就能自己造出组合命令。我自己的经验是,用这种方式记命令,比逐条死记硬背效率高得多,而且碰到没见过的组合,也能大概猜到它想干什么。
2. 高频命令速查:移动、编辑、保存退出一次说清
这一节我按“场景”而不是按“字母表”来列命令,因为你实际写代码、改配置文件的时候,脑子里想的是“我要到行尾”“我要删掉这个括号里的内容”,而不是“我要按哪个字母”。
2.1 光标移动与文本定位速查表
日常编辑里最耗时的动作就是移动光标。我整理了一份自己反复用的移动命令表,按频率排:
| 操作目标 | 命令 | 说明 |
|---|---|---|
| 字符级移动 | hjkl | 左、下、上、右,替代方向键 |
| 单词级移动 | w/b/e | 下一个单词开头 / 上一个单词开头 / 单词结尾 |
| 行首行尾 | 0/$ | 到行首第一个字符 / 到行尾最后一个字符 |
| 行首(忽略缩进) | ^ | 跳到本行第一个非空白字符,写代码时比0更常用 |
| 指定行 | :12回车 | 跳到第 12 行,配合:set number看行号 |
| 屏幕滚动 | Ctrl+d/Ctrl+u | 向下 / 向上滚动半屏 |
| 文件首尾 | gg/G | 到第一行 / 到最后一行 |
| 匹配括号 | % | 在成对的()、{}、[]之间跳转 |
| 当前行高亮 | Shift+v然后Shift+g | 从光标处选中到文件结尾,常用来快速整体拷贝 |
其中%这个键在调试代码时特别好用。你写了一大段嵌套括号,不确定自己是不是少写了一个右括号,把光标放在某个左括号上按一下%,它会跳到配对的右括号;如果跳的位置不对,说明括号不匹配,比人眼数靠谱多了。
2.2 编辑操作与保存退出命令
编辑类的操作也遵循前面说的“动词 + 动作”模式。基础部分我列几个高频命令:
x删除光标所在字符,X删除光标前一个字符;连续按x可以逐个删,但如果你想删 5 个字符,直接5x更快。dd删除整行,d3d或3dd删除从当前行开始往下 3 行。yy复制当前行,p在当前光标下方粘贴,P在当前光标上方粘贴。u撤销,Ctrl+r重做。这是新手最容易忽略的两个键。>>向右缩进当前行,<<向左缩进,选中多行后按>可以批量缩进。
保存退出是大家问得最多的,我直接给出标准命令:
| 操作 | 命令 |
|---|---|
| 保存但不退出 | :w |
| 退出不保存 | :q! |
| 保存并退出 | :wq或:x |
| 放弃所有修改强制退出 | :qa!(适合一次打开多文件时) |
| 另存为 | :w filename.txt |
注意:
ZZ(大写)也等于保存退出,而且不需要按冒号,速度更快。但如果你的目标是不保存直接走人,按ZQ比:q!更省手。
我个人的习惯是,能不用:wq就不用,因为退出前你往往还想扫一眼改动,ZZ一键搞定,少打两个字符。如果你经常忘记保存,可以在.vimrc里加一行set autowrite,这样切换缓冲区、执行:make之前 Vim 会自动保存当前文件,省掉很多手动按键。
3. 批量处理文本的高级玩法:宏、递增序列与多文件协作
普通编辑需求用上面的命令就够了,但真正让 Vim 拉开与普通编辑器差距的,是它对“重复性工作”的自动化处理。这一节的内容也是很多 Vim 用户进阶的分水岭。
3.1 用宏把重复操作打包
宏的本质是“把一串操作录下来,然后在需要的地方重放”。我举一个实际场景:假设你有一个 150 行的配置文件,每一行都以TODO开头,现在想统一改成FIXME-2025加行号的形式。手工一行行改得累死,用宏大概 30 秒就能搞定。
操作步骤是这样的:
- 把光标移到第一行要做修改的位置。
- 按
qa开始录制宏,q是宏命令,a是这个宏的名字,用字母a到z都可以。 - 正常执行修改操作,比如输入
0fT之类,或者直接ctD(把这行TODO之后的内容改掉)。 - 修改完成后,按
j移动到下一行(注意:宏里不能包含手动跳到第 N 行的动作,要用相对移动,否则重放时会一直在同一行执行)。 - 按
q结束录制。 - 再按
@a执行一次宏,或按5@a连续执行 5 次,也可以选中多行后按:normal @a批量调用。
宏最容易踩的坑是“绝对移动”问题。录制宏的时候,如果你用G跳到了文件末尾,那重放时它会再次跳回文件末尾,而不是在下一行执行,结果就是你看着屏幕上一堆操作只在一行来回跑。解决办法很简单:录制过程里只用j、k、w、b这类相对移动,别用gg、G、数字+G这类绝对跳转。
提示:如果你录了一个宏,执行到一半发现有问题,按
Esc或者Ctrl+c中断,然后u撤销刚才批量产生的改动,修正宏内容后重新录制。
3.2 在已有占位数字的一列里生成递增序列
这是我在社区里被问过很多次的问题:“vim 在已有占位数字的一列递增怎么用”。比如你有一堆行,每行末尾都有一个=1的占位数字,希望它们变成=1、=2、=3……依次递增。
先说最直观的方案:先用Ctrl+v进入可视块模式,选中那些数字所在的一列,然后按g紧接着按Ctrl+a。这个组合的意思是“对这一列所有选中的数字做加 1 操作,并且整个过程只执行一次”,Vim 会自动按从上到下的顺序生成递增数字。比如你选中了三行的数字,按完g Ctrl+a,三行会变成 1、2、3。
如果占位数字不是从 1 开始,而是希望从 100 开始递增,可以先把这列数字统一设置成 100,选中后按g Ctrl+a,结果就是 100、101、102…… 这个原理是Ctrl+a本身就给光标下的数字加 1,g前缀让它变成一个“列操作”,一次性对所有选中的行生效。
还有另一种场景:占位数字并不连续分布在每一行,而是散落在不同位置,这时候更适合用宏。录制宏的时候,把光标定位到第一个数字上,按Ctrl+a给它加 1,然后按j跳到下一行对应位置,停止录制,最后对着后面每一行批量执行@a。这种方式的好处是灵活,数字在哪一行都能处理,不用特意选中一个规整的矩形块。
如果你要一个完全不用手动选中列的方案,还可以用表达式替换。比如把文件里所有形如key_数字里的数字,按出现顺序从 1 开始递增,可以这样:
:let i = 1 | g/^key_/s//\='key_' . i/ | let i += 1这里g/^key_/匹配所有以key_开头的行,把每行的key_替换成key_加当前计数变量i,替换完i加 1。用\=开头的是 Vim 的表达式替换语法,它允许你在替换内容里写脚本逻辑,这是批量文本处理的一个大杀器,熟练之后很多繁琐的重复操作都可以在这个模式下解决。
3.3 寄存器:Vim 的剪贴板增强
初用 Vim 的人经常会发现,复制一段文字,再去别的地方粘贴,粘贴出来的却是别的内容。这是因为 Vim 的删除、复制操作默认写入了“无名寄存器”,而不是系统剪贴板。你每次执行d、c、y操作,都会覆盖无名寄存器里的内容。
寄存器可以理解成 Vim 自己的一套剪贴板,只不过它有好多个格子。最基本的几个:
"a到"z:26 个命名字母寄存器,专门用来手动存取内容。"0:最近一次y复制的内容。即使你后面连续删了几行,"0p也能把上次复制的文本粘贴出来。"+和"*:系统剪贴板,"+y是把选中内容复制到系统里,"+p是从系统里粘贴,只有编译时带剪贴板支持的 Vim 才可用。
我实际用得最多的是名字寄存器:需要把两块内容交换位置时,先把第一块用"ayy存到寄存器 a,把第二块用"byy存到寄存器 b,然后把 a 的内容粘贴到原来 b 的位置,把 b 的内容粘贴到原来 a 的位置,全程不碰系统剪贴板,互不干扰。
查看当前所有寄存器内容,可以用:reg命令。如果发现某个寄存器里录入了不该有的内容,可以用:let @a = ''清空它。
3.4 minibufexplorer 与多缓冲区切换
Vim 打开多个文件时,每个文件对应一个“缓冲区”(buffer)。缓冲区跟标签页还不一样:标签页更像浏览器的多个标签,缓冲区则是 Vim 内部维护的“打开文件池”。
新手最常用的是:e filename打开文件,Ctrl+^在最近两个文件之间切换,Ctrl+w后面接方向键在窗口之间跳,Ctrl+w松手再按s或v横竖分屏。这些组合虽然好用,但文件一多还是容易迷路,这时候就轮到 minibufexplorer 这类插件出场了。
minibufexplorer(常简写为 MBE)是一个老牌的缓冲区管理插件,它会在编辑窗口顶部自动显示一条缓冲区列表,每个文件显示为[文件名]的形式。当前所在文件会用不同的样式高亮,方便你一眼看出自己在哪个文件里。
常用操作:
- 用鼠标左键点击列表里的文件名,可以直接跳到对应缓冲区。
- 在列表中点击文件名旁边的
x,可以关闭对应缓冲区。 - 如果不习惯用鼠标,也可以用命令
:MBE手动刷新列表,配合:bn(下一个缓冲区)、:bp(上一个缓冲区)、:bd(删除当前缓冲区)使用。
MBE 的配置比较轻量,我常用的几行写在.vimrc里:
let g:miniBufExplMapWindowNavVim = 1 let g:miniBufExplMapWindowNavArrows = 1 let g:miniBufExplMapCTabSwitchBufs = 1 let g:miniBufExplModSelTarget = 1这几行打开了用Ctrl+方向键切换窗口、用Ctrl+Tab切换缓冲区的功能。如果你是老式键盘操作习惯,MBE 顶部的列表本身不占太多空间,我用了几年没觉得碍眼。如果只想保持界面干净,也可以开着:set hidden,这样切缓冲区之前不会因为文件未保存而被 Vim 拦下来,配合 MBE 会很顺畅。
注意:如果你不用插件,只靠内置命令,也可以通过
:ls查看所有缓冲区列表,:b 3直接跳到编号为 3 的缓冲区,:b 文件名的一部分按模糊匹配跳转。内置缓冲区功能本身就已经很强了,MBE 只是把列表可视化,让你能“看得见摸得着”。
4. Windows 环境下安装 Vim 与基础配置
很多刚接触 Vim 的人是在 Windows 上开始的。这里有个容易混淆的点:Windows 下常见的 gvim 和命令行里的 vim 其实是同一个程序的两个入口,gvim 带图形界面,vim 是纯命令行版。日常写代码、改配置,我更推荐直接用 gvim,因为剪贴板、字体、配色这些功能在图形界面下都好配置。
4.1 两种最省事的安装方式
第一种是到 vim.org 官网的下载页找gvim的自安装可执行文件。安装时默认会装到C:\Program Files (x86)\Vim之类的位置,安装包会问你“创建右键菜单”“加入 PATH”这些选项,建议全选。
第二种是如果你有 Windows 的包管理器,用命令行安装更省事:
choco install vim或者:
winget install vim.vim装完之后,在cmd或者 Windows Terminal 里直接敲vim就应该能唤起 Vim 了。如果没有,大概率是 PATH 没生效,重开一个终端窗口再试一次,或者手动把 Vim 的安装目录加到系统环境变量里。
安装环节最容易出问题的不是装不上,而是配置文件位置找不到。Windows 下 Vim 默认的配置目录取决于你用的 Vim 版本, 但通常会在你的用户目录下。比较可靠的办法是打开 Vim 后执行:
:echo $HOME :echo $VIM第一条命令看用户目录在哪,第二条看 Vim 安装目录在哪。配置文件一般放在用户目录下,文件名叫_vimrc,而不是 Linux 下的.vimrc。如果你用vimfiles目录管理插件,也要注意路径分隔符统一用反斜杠还是正斜杠,建议全用正斜杠,省得转义出问题。
4.2 一份开箱即用的入门配置
我整理了一份自己在 Windows 机器上常用的精简配置,可以直接复制到_vimrc里:
set nocompatible set encoding=utf-8 set fileencodings=utf-8,gbk,gb2312,gb18030 set number set relativenumber set tabstop=4 set shiftwidth=4 set expandtab set smartindent set ignorecase set smartcase set incsearch set hlsearch set clipboard=unnamedplus syntax on逐条解释一下为什么要这么设:
nocompatible必须放在最前面,关闭 Vim 的 vi 兼容模式。不关的话,方向键在普通模式会变成ABCD这样非常诡异的行为。encoding=utf-8设置 Vim 内部使用的编码,fileencodings是打开文件时自动检测编码的顺序。Windows 上很多旧文件是 GBK 编码,把 GBK 系列放在 utf-8 后面,可以兼顾打开 UTF-8 文件和 GBK 旧文件。number和relativenumber同时开启,会让你既能看到当前行号,又能看到其他行离当前行的相对距离。配合3j、5k这类相对移动命令,跳转效率能大幅提升。clipboard=unnamedplus把 Vim 的无名寄存器直接绑定到系统剪贴板。这样你在 Vim 里yy复制,到别的程序里Ctrl+v粘贴,可以直接生效,省掉"+前缀的麻烦。syntax on开启语法高亮,写代码和配置文件的时候看着不累。
配置完了按:source $MYVIMRC重新加载,立刻生效,不用重启 Vim。
4.3 装完以后必做的按键与编码检查
Windows 上有两个问题我踩过很多次坑,这里提前说,省得你再去查。
第一个是方向键失灵。如果你发现编辑时按上下左右出来的是ABCD,先检查.vimrc(或_vimrc)里有没有set nocompatible,如果没有,加上这一行基本就好了。
第二个是中文显示乱码。打开一个文件发现全是乱码,不要急着改文件,先检查set fileencodings里有没有把 GBK、GB18030 这类编码放进去。如果文件本身是 UTF-8,但 Vim 内部编码被改成了 GBK,也会乱。稳妥的组合是:
set encoding=utf-8 set fileencodings=utf-8,gbk,gb2312,gb18030这样 Vim 内部统一用 UTF-8 处理,打开文件时自动按顺序探测真实编码。
提示:Windows 自带的命令提示符有时会把 Vim 的界面显示得很难看,建议直接用 Windows Terminal,配色和字体渲染都更好,中英文混排也不会出现大空格对齐问题。
5. 使用过程中常见的坑与排查记录
最后这部分我把这几年在同事、学员那里收集到的高频问题整理成一份排查清单。这些问题本身不难,但第一次遇到时确实会卡住好一会儿。
5.1 保存退出类问题
问题一:退出时提示 “E37: No write since last change”
说明当前文件有未保存的修改。Vim 默认不允许直接:q退出,防止你手滑丢了工作。解决办法是明确告诉它你的意图:保存退出用:wq,放弃修改退出用:q!。
问题二:保存时提示 “E212: Can't open file for writing”
这个基本是权限问题。你打开的是一个只有只读权限的系统配置文件,普通模式下:w保存,系统不让写。如果你确信必须修改这个文件,Linux/macOS 下有一种常见写法是:
:w !sudo tee %把当前缓冲区通过 sudo 重新写入原文件。这行命令的意思是把当前内容作为标准输入,交给sudo tee命令写回磁盘。Windows 下遇到权限不够,直接右键 Vim 图标“以管理员身份运行”,再重新打开文件修改。
问题三:改了文件但:q不提示,文件没保存就跑了
这种情况通常是因为 .vimrc 里开了set hidden。hidden允许缓冲区未保存时切换,但不等于自动保存。建议搭配set autowrite使用,这样切换缓冲区时 Vim 会自动把修改写回对应文件,避免丢失。
5.2 编辑与粘贴类问题
问题一:粘贴远程复制来的一长段代码,缩进全乱了
Vim 的smartindent会在你粘贴时自动做缩进,但自动缩进是基于代码结构的,从别处拷贝来的代码可能触发了错误的缩进。解决方法是粘贴前先进入“不做任何自动处理”的粘贴模式:
:set paste粘贴完记得关掉:
:set nopaste手动执行总容易忘,可以在.vimrc里设置一个快捷键,按 F6 在 paste 和 nopaste 之间切换:
set pastetoggle=<F6>问题二:按退格键删不掉自动缩进的空格
这个跟backspace选项有关。Vim 默认禁止你删除当前行在indent和insert模式下自动插入的内容。想改掉这个“死板”的行为,在配置里加上:
set backspace=indent,eol,start这句命令表示退格键可以删除缩进、删除换行符、删除当前行插入开始之前的内容。
问题三:用Ctrl+v选了多行,却只修改了第一行
很多人想对一列数字做批量操作,选中了可视块,却发现按x只删除了一个字符。原因是你没有用g前缀。在可视块模式下,普通编辑命令(比如x、d、c)都会作用于选中的整个块,但像I、A、r这类操作有特殊含义,比如I是“在选中的块内所有行前面插入”,而不是单纯把光标移到首行。遇到这种粒度的操作差异,最快的办法是:help visual-block查一下帮助文档,Vim 自带的帮助系统非常详细,比网上零散回答更权威。
5.3 输入法与编码相关注意事项
在 Windows 上使用 Vim 时,中文输入法有时会“吃掉”命令键。比如普通模式下按Esc从 insert 模式退出,如果此时正处于中文输入状态,Vim 可能只看到一个特殊字符而不是Esc,界面就会卡在插入模式。
解决办法有两个方向:一是养成“退出 insert 模式之前先把输入法切到英文”的习惯;二是装一些输入法辅助插件,或者用Ctrl+^之类的按键在某些环境里临时切换。我自己更依赖配置层面的方案:在_vimrc里把Esc映射成Esc的同时,尽量让输入法保持在英文状态。
编码方面还有一个细节:如果文件里有中文注释,在 Windows 下查看没问题,但用 Git 提交后别人看到的是乱码,这多半是 Git 的 core.quotepath 或者文件本身编码不统一造成的,跟 Vim 关系不大。建议团队里约定所有源文件统一 UTF-8 保存,Vim 里:set bomb可以给文件加 UTF-8 BOM 标记,某些环境下用 BOM 能减少识别混乱,但纯 Unix 环境下反而可能冲突,所以除非必要,一般不加 BOM。
5.4 Vim 和 Xcode 这类 IDE 的关系,我的看法
经常有人问“有了 Xcode、VS Code 为什么还要学 Vim”,这个问题我问过自己很多遍。我的观点是:Vim 解决的是“文本编辑”这层问题,IDE 解决的是“工程开发”这层问题。你在 Xcode 里写 Swift,需要的是编译、调试、界面设计、模拟器这些能力,Vim 在这些方面替代不了 IDE;但如果你通过 SSH 登上一台服务器改配置,或者在终端里快速修改一段脚本、批量调整一个日志文件,Vim 的效率优势立刻就会体现出来。
即使是 IDE 用户,也推荐把 IDE 里的 Vim 模式插件打开,比如 VS Code 的 Vim 插件、JetBrains 系的 IdeaVim、Xcode 的 XVim2。这样你既保留了 IDE 的高层功能,又能用 Vim 的编辑方式处理文本。很多人觉得 Vim 模式在 IDE 里卡顿、手势不完整,其实是配置没跟上,比如 Esc 响应慢、宏录制不生效,大概率是插件版本或按键冲突问题,属于可以解决的问题,不需要因此劝退。
我个人的工作流是:日常写业务代码用 IDE 加 Vim 插件,改服务器配置、处理数据文件、写快速脚本一律开终端 Vim。两种环境切换了这么多年,最大的感受是,Vim 的那套“动词 + 动作”思维方式一旦形成,不管在哪个工具里,你都会不自觉地用更小的操作单元去处理文本,这个收益比编辑器本身能列出的功能列表要大得多。