今日主题#
- 主主题:
命令行模式与常用 Ex 操作 - 副主题:把前几天学过的搜索、替换、buffer、window 接到统一的命令行心智模型上
学习目标#
- 先分清“普通模式是在文本里操作”,“命令行模式是在对编辑器下命令”。
- 掌握最常用的一层 Ex 工作流:
- 保存、退出、重新打开文件
- 给命令指定作用范围
- 把多个命令串起来执行
- 在命令历史和命令行窗口里回看与修改
- 建立一个简单判断:什么时候该继续用
d、c、文本对象,什么时候该上:。
前置回顾#
- Day 003 学的是
operator + motion,是在“当前光标附近怎么改”。 - Day 004 学的是文本对象,开始按结构改内容。
- Day 005 学的是搜索、替换、Visual,开始对“一个区域”做修改。
- Day 006 学的是 buffer / window / split,开始处理多个上下文。
- Day 007 往前再推一步:不只是“在文本里动手”,而是“告诉编辑器对哪些行、哪些文件、哪些窗口做什么”。
典型场景#
- 你改完文件,想保存但不离开当前编辑。
- 你想退出当前文件,但不想保存本次误操作。
- 你想把第 10 行到第 20 行统一缩进或替换。
- 你刚做过一次
:%s/old/new/gc,想把命令拿回来稍微改一下再执行。 - 你想先列出 buffer,再跳到某个 buffer,或者在一个命令里连续做两步整理。
如果说普通模式更像“直接上手”,那么命令行模式更像“给编辑器发指令”。
最小命令集#
今天只保留高频、真正能马上用上的一层。
进入命令行模式#
:- 进入 Ex 命令行
/、?- 搜索本身也属于命令行模式
q:- 打开命令行窗口,回看和编辑之前输入过的 Ex 命令
q/、q?- 打开搜索命令行窗口,回看和编辑之前的搜索
- 命令行中
Ctrl-F- 从当前命令行直接进入命令行窗口
最常用文件命令#
:w- 保存当前文件
:q- 退出当前窗口
:wq- 保存并退出
:q!- 强制退出,不保存本次修改
:e file- 打开文件
:ls- 列出 buffer
范围#
:.- 当前行
:$- 最后一行
:%- 整个文件
:1,5- 第 1 到第 5 行
:.,$- 从当前行到文件末尾
:/pattern/,$- 从匹配某个模式的行到文件末尾
- Visual 选区下的
:'<,'>- 上一次或当前选区范围
组合与批量#
:%s/old/new/gc- 全文件替换并逐个确认
:1,5s/foo/bar/g- 只在某个范围替换
:g/pattern/p- 对匹配行执行命令,今天先把它当成“按条件筛行并执行”
:ls | b 3- 用
|串联多个 Ex 命令
- 用
命令行编辑与历史#
Ctrl-B/Ctrl-E- 在命令行里跳到开头 / 结尾
Ctrl-W/Ctrl-U- 删除前一个词 / 删除到行首
Ctrl-R Ctrl-W- 把光标下单词直接插进命令行
:history- 查看命令历史;也可用
:his :、:his /
- 查看命令历史;也可用
@:- 重复上一条命令行命令
与外部命令协作#
:!cmd- 临时执行外部命令,先看结果,不改当前缓冲区
:r !cmd- 把外部命令输出读入当前缓冲区
:{range}!cmd- 把一个范围的文本交给外部命令过滤,并用结果替换原文本
:w !cmd- 把当前缓冲区内容写到外部命令的标准输入
:grep pattern- 用
grepprg搜索并把结果送进 quickfix,比单纯:!grep更像 Vim 工作流
- 用
它是怎么用的#
命令行模式不只是冒号#
本地 cmdline.txt 里把 command-line mode 归到一类:
:Ex 命令/和?搜索!过滤或调用外部命令
也就是说,Day 005 学过的搜索,其实已经在用命令行模式了。今天只是把这件事说清楚,并把 : 这一支补齐。
Ex 命令的基本句式是“范围 + 命令”#
最容易抓住的心智模型是:
:[range]command比如:
:w
:%s/old/new/gc
:1,5d
:.,$>这几条命令背后都是同一件事:
- 先决定作用到哪里
- 再决定做什么
这和 Day 003 的 operator + motion 很像,只不过:
- 普通模式里常见的是“操作符 + 运动”
- Ex 里常见的是“范围 + 命令”
范围是 Ex 最该先练顺的部分#
本地帮助里明确给出了高频地址:
{number}.$%/pattern/?pattern?
以及偏移:
.+3/that/+1
实战里先会这几个就很够用:
:1,5p
:.,$d
:%s/foo/bar/g
:/TODO/,$s/foo/bar/gc真实场景理解:
:1,5p看前 5 行:.,$d从当前行删到文件末尾:%s/foo/bar/g全文件替换:/TODO/,$s/foo/bar/gc从某个 TODO 开始到结尾逐个确认替换
| 让命令行从“单步”变成“一个短流程”#
本地 cmdline.txt 说明 | 可以分隔多个命令。
例如:
:ls | b 3它的价值不在于炫技,而在于把一个很短的编辑流程串起来:
- 先看 buffer 列表
- 再跳到目标 buffer
你也可以这样理解:
- 普通模式是一步一步手上做
- Ex 更适合把短流程压成一句话
命令行窗口是“把临时命令变成可编辑历史”#
q: 很值得早一点记住。
场景是:
- 你刚打了一条很长的替换命令
- 发现只想改一个范围或一个 flag
- 不想从头重打
这时可以:
- 输入
q: - 在命令行窗口里找到之前的命令
- 像编辑普通文本一样改它
- 回车执行
命令行里按 Ctrl-F 也能进去。这个动作能明显降低“复杂命令输错就全部重来”的压力。
命令行本身也有一套很实用的编辑键#
本地 cmdline.txt 里,命令行并不是“输错了就整条删掉重来”的一次性输入框。
最常用的就是这几组:
Ctrl-B- 跳到命令行开头
Ctrl-E- 跳到命令行结尾
Ctrl-W- 删掉光标前一个词
Ctrl-U- 删到行首
这在长替换命令里很值钱,因为你经常只是:
- 范围写错了
- 一个 flag 想补上
- 某个单词想改掉
而不是整条命令都得重输。
Ctrl-R Ctrl-W:把光标下单词直接塞进命令行#
本地 cmdline.txt 还给了一个很实用的命令行补词动作:
Ctrl-R Ctrl-W- 把光标下的 word 插进当前命令行
这对下面这些场景特别省事:
- 光标已经停在某个变量名上,准备
:%s/old/new/g - 光标已经停在某个配置键上,准备
/或:grep
例如你在普通模式光标停在 timeout 上,然后输入:
:%s/接着按:
Ctrl-R Ctrl-W就能把 timeout 直接带进命令行,不用重新手打一遍。
:history、q/、@::Ex 和搜索都可以回看和重跑#
Learn-Vim 的命令行章节和本地帮助都强调了一个点:
- 命令行历史不是只有
q:
你至少可以先会这三组:
:history- 看 Ex 历史
q/或q?- 看搜索历史并直接改
@:- 重复上一条命令行命令
所以如果你刚刚跑过:
:%s/foo/bar/gc接下来想原样再跑一次,或者只改一点点,就不必从头重打。
:!、:r !、:{range}!、:w ! 是 Vim 和命令行工具最自然的接口#
本地 cmdline.txt 直接把这些形态并列列出来了:
:!cmd:r !cmd:w !cmd
同一个帮助文件里还给了非常典型的例子:
:!ls | wc
:r !ls | wc
:r !date本地 change.txt 还单独说明了 filter 命令:
!{motion}{filter}:{range}!{filter}
也就是说,命令行模式和外部命令协作,大致可以分成四种心智模型:
- 只看外部命令输出,不改缓冲区:
:!grep -n TODO %
:!head -n 20 %
:!tail -n 30 /var/log/app.log- 把外部命令输出插入到文本里:
:r !date
:r !head -n 5 README.md
:r !grep -n ERROR /var/log/app.log | tail -n 10- 把一段文本交给命令处理后替换原文:
:%!awk 'NF'
:%!sort | uniq
:'<,'>!awk '{print toupper($0)}'- 把当前缓冲区当作标准输入送给命令:
:w !wc -l
:'<,'>w !cat
:%w !awk 'END { print NR }'这里最容易混的地方是:
:!cmd是“我去看命令输出”:r !cmd是“我把命令输出拿回来放进文本”:{range}!cmd是“我把这段文本交给命令改完再放回来”:w !cmd是“我把当前文本喂给命令”
如果你一开始先把这四种动作分清,命令行模式就会一下子具体很多。
常见操作套路#
套路 1:改完就保存,但继续留在当前上下文#
场景:
- 你在一个文件里已经改完这一轮
- 不想退出,只是先落盘
命令:
:w这是最基础但最高频的 Ex 命令。
套路 2:只对当前到文件末尾做处理#
场景:
- 你光标已经移动到一个段落中间
- 想从这里往后统一处理
命令:
:.,$s/foo/bar/gc这个句式很有代表性,因为它把:
- 当前定位能力
- 搜索替换
- Ex 范围
三件事连起来了。
套路 3:先选中,再交给 Ex#
场景:
- 你肉眼很容易框出一段代码
- 但对这段内容想做的是 Ex 命令,比如替换、缩进、格式化
做法:
- 先用
V或Ctrl-V选区 - 按
: - Vim 会自动补出
:'<,'>
例如:
:'<,'>s/foo/bar/g
:'<,'>>这套很适合把 Visual 和命令行模式接起来。
套路 4:看完历史命令后再微调重跑#
场景:
- 你刚跑过一次很长的
:s - 想把范围从
%改成.,$ - 或者把
g改成gc
做法:
q:- 找到上一条命令
- 修改后执行
这比重新打一遍更稳。
套路 5:buffer 工作流和 Ex 串联#
场景:
- 你想看 buffer 列表后立刻跳转
命令:
:ls | buffer 2Day 006 学的是“怎么切”,Day 007 学的是“怎么把切换也纳入一条命令”。
套路 6:要“看外部世界”,先用 :!cmd#
场景:
- 你不想离开 Vim
- 只是想临时看一下 shell 命令结果
命令:
:!grep -n TODO %
:!head -n 20 %
:!tail -n 50 /var/log/nginx/access.log这类命令的重点是:
- 先看
- 不改缓冲区
- 看完回到当前编辑
如果你只是临时查一下文件内容、日志尾部、某个关键字,这通常是最轻的一种协作方式。
套路 7:要“把结果带回文本”,用 :r !cmd#
场景:
- 你要在文档里插入命令结果
- 或者要摘一小段外部输出做记录
命令:
:r !date
:r !head -n 5 ~/.bashrc
:r !grep -n ERROR app.log | tail -n 10真实场景包括:
- 写笔记时插入当前日期
- 插入某个文件头部几行做引用
- 把日志里最近几条匹配结果贴进排查记录
这个动作很适合把命令行输出变成“编辑素材”。
套路 8:要“改文本本身”,用 filter 命令#
场景:
- 你已经在缓冲区里有一段文本
- 现在想借助 Unix 工具快速改形状
命令:
:%!awk 'NF'
:%!sort | uniq
:'<,'>!awk '{print NR ": " $0}'理解方式是:
:%!awk 'NF'- 删除空行,只保留有内容的行
:%!sort | uniq- 整个文件排序后去重
:'<,'>!awk '{print NR ": " $0}'- 只改选中的那段文本
这个模式和 Day 003 / Day 007 很接近:
- Day 003 是“操作 + 范围”
- Day 007 是“范围 + 命令”
- filter 是“范围 + 外部命令”
套路 9:要“把当前文本喂给命令”,用 :w !cmd#
场景:
- 你想把缓冲区内容交给某个命令处理
- 但不一定要把结果写回缓冲区
命令:
:w !wc -l
:'<,'>w !cat
:%w !awk 'END { print NR }'这类动作适合:
- 快速统计行数
- 验证某个范围到底会传给命令什么内容
- 把当前文件内容送给某个接收标准输入的工具
其中 :'<,'>w !cat 虽然看起来简单,但很适合练习理解:
- 你写出的到底是整个文件,还是某个选区
:w !cmd到底是“保存文件”还是“发给命令”
套路 10:grep 优先分清 :!grep 和 :grep#
场景:
- 你只是想看 grep 输出
- 或者你想进入 Vim 的 quickfix 工作流
对比:
:!grep -n TODO %
:grep TODO %区别是:
:!grep- 更像临时 shell 调用,只是看输出
:grep- 会走 Vim 的
grepprg,并把结果送进 quickfix
- 会走 Vim 的
所以:
- 临时看结果,用
:!grep - 想继续在结果里跳转、逐个处理,用
:grep
环境差异:vim / nvim / LazyVim#
Vim 和 Neovim 这一层基本一致#
今天这组能力主要属于 Vim/Neovim 的共同底层:
:w:q:e- 范围
:s|串联q:命令行窗口
本地确认环境是:
Vim 9.2Neovim 0.12.0
这些基础语义在两边是一致的。
补充一点:
:!、:r !、:{range}!、:w !这些接口在vim和nvim里都成立- 真正变化更大的通常不是 Vim 这一侧,而是外部 shell 本身
当前本机是 Windows / PowerShell,所以这一节新增的 grep、awk、cat、head、tail 例子,主要按 Unix-like shell 场景讲解;Vim 这一层接口语义来自本地帮助文档,命令写法则以 Unix 常见工作流为主。
Neovim 进入后,命令行模式不会消失#
到了 Neovim,你可能更常:
- 用 Telescope 找文件
- 用 LSP 跳定义
- 用终端模式跑命令
但真正进入缓冲区后,很多收尾动作还是会落回:
:w:q:%s:'<,'>
所以 Day 007 学的不是“过时命令”,而是后面所有工作流的稳定落点。
LazyVim 会把很多入口做得更快,但不会替代 Ex 的表达力#
LazyVim 以后会给你:
- 文件搜索入口
- buffer 切换入口
- 项目内搜索入口
但当你已经进入编辑细节,需要表达“从这里到那里,对这批行做这件事”时,Ex 仍然是最短语言。
今日练习(5-10 分钟)#
练习材料#
新建一个临时文件,内容如下:
alpha one
alpha two
beta one
beta two
todo start
alpha three
beta three
alpha four练习任务#
- 用
:w保存一次。 - 用
:1,4p查看前四行。 - 把第 1 到第 4 行里的
alpha改成item:
:1,4s/alpha/item/g- 把从
todo start所在行到文件末尾的beta改成task,并逐个确认:
:/todo start/,$s/beta/task/gc- 用 Visual 行模式选两三行,按
:,观察自动出现的:'<,'>。 - 对选中的行执行一次替换或缩进。
- 输入一条稍长的替换命令,再用
q:打开命令行窗口,把它改短后重新执行。 - 最后试一次:
:ls | b 1- 光标停在某个已有单词上,输入
:%s/,再用Ctrl-R Ctrl-W把它插进命令行。 - 试一次
:history,再试一次@:.
补充练习:命令行工具协作(Unix 场景)#
如果你平时会在 Unix / Linux / macOS shell 里工作,再补一轮这组:
- 只看,不改缓冲区:
:!head -n 5 %
:!grep -n alpha %- 把结果插进文本:
:r !date
:r !tail -n 3 %- 把整个文件交给外部命令过滤:
:%!awk 'NF'
:%!sort | uniq- 选中几行后,只过滤选区:
:'<,'>!awk '{print "[" NR "] " $0}'- 把当前文本送进命令,而不是替换文本:
:w !wc -l完成标准#
- 能说出 Ex 的基本句式是“范围 + 命令”。
- 能分清
%、.,$、'<,'>各自代表什么范围。 - 能把 Visual 选区交给
:继续处理。 - 能在复杂命令输错后想到
q:,而不是从头重打。 - 能在命令行里自然使用
Ctrl-B、Ctrl-E、Ctrl-W、Ctrl-U。 - 能在合适的时候想到
Ctrl-R Ctrl-W和@:. - 能分清
:!cmd、:r !cmd、:{range}!cmd、:w !cmd的区别。
今日问题与讨论#
我的问题#
- 暂无。本节先把 Day 007 的最小心智模型搭起来,后续问答直接补在这里。
外部高价值问题#
问题 1:Ex 范围和普通模式的 motion,本质上是不是一回事?#
- 问题:
- Day 003 学的是
d2w这种“操作 + 范围”,Day 007 又变成了:1,5d。这两者是不是同一种思路?
- Day 003 学的是
- 简答:
- 是同一类思路,但表达层不同。普通模式更偏局部、即时;Ex 更偏明确指定一段行范围后批量执行。
- 场景:
- 局部删几个词,用
dw更快。 - 已经知道是第 20 到第 40 行都要处理,用 Ex 更直接。
- 局部删几个词,用
- 依据:
- 本地
cmdline.txt的 Ex range 说明。 - Day 003 的
operator + motion心智模型。
- 本地
- 当前结论:
- 可以把 Ex 理解成“面向行范围和批量动作的另一种编辑句式”。
- 是否需要后续回看:
- 需要。等进到
:global、:vglobal、:normal时会更清楚。
- 需要。等进到
问题 2:为什么 Visual 选区后按 :,会自动带上 '<,'>?#
- 问题:
- 这个范围到底表示什么?
- 简答:
- 它表示当前或上一次 Visual 选区的起点和终点。
- 场景:
- 你已经框出一段文本,接下来不想手工数行号,而是直接对选区执行替换、缩进、打印。
- 依据:
- 本地 Ex range 机制和 Visual 选区行为。
- 当前结论:
:'<,'>是把 Visual 选区无缝交给 Ex 的桥梁。
- 是否需要后续回看:
- 需要。后面进入代码格式化或批量注释时会频繁用到。
问题 3:Vim 里怎么和 Unix 命令行工具配合?#
- 问题:
- 比如
grep、awk、cat、head、tail这些,应该在 Vim 里怎么接?
- 比如
- 简答:
- 先按四类动作分:
:!cmd看输出:r !cmd读输出进缓冲区:{range}!cmd过滤文本并替换:w !cmd把文本送给命令
- 先按四类动作分:
- 场景:
:!tail -n 20 app.log- 临时看日志尾部
:r !date- 把日期插进笔记
:%!awk 'NF'- 清理空行
:w !wc -l- 把当前文件内容送给命令统计
- 依据:
- 本地
cmdline.txt对:!cmd、:r !cmd、:w !cmd以及|的示例 - 本地
change.txt对 filter 命令的说明
- 本地
- 当前结论:
- 对命令行工具的协作,本质上还是 Day 007 的同一套句式,只是把“命令”换成了“外部命令”。
- 是否需要后续回看:
- 需要。后面如果进入
:make、quickfix、Neovim 终端,再回看会更顺。
- 需要。后面如果进入
问题 4:为什么 q: 不够,还要记 q/ 和 @:?#
- 问题:
- 我已经会
q:了,为什么还要再补搜索历史和“重复上一条命令”?
- 我已经会
- 简答:
- 因为命令行模式不只承载
:,还承载/、?和外部命令;它们也都值得回看与重跑。
- 因为命令行模式不只承载
- 场景:
- 你刚做过一次搜索,想把模式稍微改一下;或者刚执行过一条 Ex 命令,想原样再跑一次。
- 依据:
- 本地
cmdline.txt对q:、q/、q?的说明。 - 本地
repeat.txt与change.txt对@:的说明。
- 本地
- 当前结论:
q:管 Ex 历史,q/管搜索历史,@:管重复上一条命令行命令。
- 是否需要后续回看:
- 需要。等后面进入更复杂的替换、全局命令和 quickfix,会更常用。
常见误区或易混点#
- 误区 1:把命令行模式理解成“只会保存退出”。
- 实际上搜索、范围替换、批处理都在这里。
- 误区 2:一看到
:就想背很多命令。- 今天先抓最常用的几条,不需要背 Ex 百科全书。
- 误区 3:
:%s会了,就以为范围不重要。- 真正常用的是“不是整文件,而是某个范围”。
- 误区 4:复杂命令输错后从头重打。
- 先想到
q:或命令行里的历史回看。
- 先想到
- 误区 5:把
:w! file和:w !cmd混掉。- 前者是强制写文件,后者是把内容写给外部命令,中间那个空格很关键。
- 误区 6:看到外部命令就默认用
:!cmd。- 如果你是想把结果带回文本,应该优先想到
:r !cmd;如果是想改文本本身,应该想到:{range}!cmd。
- 如果你是想把结果带回文本,应该优先想到
扩展内容#
:r !cmd- 把外部命令输出读入当前缓冲区,例如插入一个命令结果。
:!cmd- 临时执行外部命令。
:{range}!cmd- 把指定范围通过外部命令过滤,例如
:%!sort。
- 把指定范围通过外部命令过滤,例如
:grep- 比
:!grep更接近 Vim 工作流,因为结果会进入 quickfix。
- 比
:g/pattern/cmd- 对匹配行批量执行命令,后面可以专门展开。
Ctrl-R a、Ctrl-R Ctrl-L、Ctrl-R Ctrl-F- 命令行里还能插寄存器、当前行、当前文件名,后面可作为提速点。
今日小结#
- 今天真正要建立的不是“背多少条冒号命令”,而是一个新句式:
:[range]command
- 普通模式解决的是“眼前这一下怎么改”。
- Ex 解决的是“对哪一批内容做什么”。
- 一旦把这个心智模型立住,前面的搜索、替换、Visual、buffer 管理都会开始变得更统一。
明日衔接#
- 下一天先不急着进入
Neovim。 - 会先进入
Vim 高频进阶整合,把前 7 天里已经碰到、但还没系统收进去的高频能力补齐:- 插入模式里的高频入口
.、撤销、寄存器- 宏的最小工作流
:global- 多文件批处理
- 这样再进入
Neovim时,会更像是在换宿主环境,而不是一边补 Vim 基础一边学新环境。
复习题#
- 命令行模式只包含
:吗?/和?算不算? - Ex 最基础的句式是什么?
%、.,$、'<,'>分别代表什么范围?- 什么时候
dw更合适,什么时候:1,5d更合适? - 复杂命令打错后,除了重输,还有什么更稳的办法?


