共240行
2026-03-29 16:02:40
Neovim 的 :global(缩写 :g)是一个基于模式匹配的批量执行命令。它本质上是一个“过滤器”:先扫描整个文档,找出匹配指定模式的行,然后在这些目标行上执行另一个 Ex 命令。
:g/pattern/command
:g!/pattern/command " 匹配不包含 pattern 的行(取反)
执行流程:
pattern。command。| 参数 | 说明 |
|---|---|
:g/^#/d |
删除所有以 # 开头的行(如注释) |
:g!/^def/normal dd |
对非 def 开头的行执行 dd(删除) |
:1,50g/error/d |
仅在 1-50 行范围内搜索并删除含 error 的行 |
:g/pattern/p |
打印所有匹配行(等效于 :grep 预览) |
:g//t$ |
将匹配行复制到文件末尾(t 为 copy) |
需求:删除文件中所有空的 console.log() 语句。
" 匹配包含 'console.log()' 且括号内为空的行,并删除
:g/console.log()/d
需求:在所有 print 语句前加上 # 将其注释掉。
" 先匹配 print 开头的行,然后执行替换命令
:g/^print/s/^/# /
需求:将 CSS 属性按选择器分组并排序。
" 1. 删除所有空行
:g/^$/d
" 2. 在每个 '}' 后插入空行(分隔选择器)
:g/^}/normal o
" 3. 对每个选择器块({ 到 })内部按字母排序
:g/{/,/}/sort
需求:从日志文件中提取所有 ERROR 级别的行到新文件。
" 将所有 ERROR 行复制到文件末尾
:g/ERROR/t$
" 或直接写入到新文件(先保存当前文件)
:g/ERROR/w >> errors.log
normal 命令执行普通模式操作(如 :g/foo/normal @a 在匹配行执行宏 a)。:g/pattern/p 预览匹配结果,确认无误后再执行危险操作(如 d 删除)。/、.)需用 \ 转义(如 :g/\/\/\//d 删除含 /// 的行)。global 命令被视为一次操作,执行后按 u 会撤销所有更改,请务必先保存文件。若需更复杂的文本处理(如跨行匹配),
global可能力不从心,建议结合sed或awk等外部工具。
Neovim 的 :global 命令本质是**“先筛选行,再执行命令”,它本身不产生“多光标”界面,但能实现批量行级编辑**。以下是结合 :s(替换)命令的实战场景。
global + substitute" 在匹配 /pattern/ 的所有行上执行替换
:g/pattern/s/old/new/g
需求:将函数内部变量 temp 改为 ctx,但避免修改函数外的同名变量。
" 匹配 function 和 end 之间的行,并替换
:g/function/,/end/s/\<temp\>/ctx/g
\<temp\> 确保只匹配单词边界(避免匹配 template)。, 范围语法:对从 function 到 end 的每一块区域执行内部替换。需求:为所有 TODO 注释行行首添加 [P1] 标记。
" 匹配包含 TODO 的行,在行首插入
:g/TODO/s/^/[P1] /
^ 匹配行首,实现“插入”而非“替换”效果。需求:清理代码行尾空格,但保留 Markdown 表格对齐用的空格。
" 匹配非 '|' 开头的行,删除行尾空白
:g!/^|/s/\s\+$//
g! 表示取反(vimgrep 风格),匹配不以 | 开头的行。\s\+$ 匹配行尾的一个或多个空白字符。需求:将 Python 字典键名统一加引号,并删除值后的逗号。
" 1. 先给键加引号
:g/^\s*\w\+:/s/\(\w\+\):/"&"/
" 2. 再删除行尾逗号(仅限非最后一行)
:g/^\s*".*",$/s/,$//
& 在替换中代表整个匹配内容。| 特性 | :global |
多光标插件(如 nvim-cmp) |
|---|---|---|
| 操作维度 | 行级(Line-wise) | 字符级(Character-wise) |
| 可视化 | 无预览,直接执行 | 实时显示多个光标位置 |
| 灵活性 | 适合规则统一的批量操作 | 适合非规律、手选位置的编辑 |
如果你需要可视化点选多个位置再编辑,应使用
vim-visual-multi或nvim-cmp的多光标模式,而非:global。
Emacs 不仅完全支持正则匹配,而且其正则引擎比 Vim 更强大(支持更复杂的语法)。但 Emacs 没有名为 global 的命令,它通过 “标记 + 操作” 的组合逻辑来实现类似功能。
Emacs 的正则表达式(Regexp)语法与 Vim 大部分兼容,但功能更丰富。
| 功能 | Emacs 语法 | Vim 对比 | 说明 |
|---|---|---|---|
| 基本匹配 | foo|bar |
同 Vim | 匹配 foo 或 bar |
| 分组 | \(group\) |
同 Vim | 捕获分组 |
| 非贪婪 | .*? |
同 Vim \{-} |
匹配尽可能少的字符 |
| 字符类 | [[:alpha:]] |
同 Vim | Unicode 字符类 |
| 语法类 | \s- (空白) |
无直接对应 | 更智能的空白匹配 |
| 高级特性 | \\(?1:foo\\) (分支) |
不支持 | Emacs 独有的复杂分组 |
常用交互命令:
M-x re-builder:实时正则构建器(输入正则式,高亮显示所有匹配)。C-M-s / C-M-r:向前/向后增量搜索正则(最常用)。M-x occur:提取所有匹配行(类似 Vim 的 :g/pattern/p)。Emacs 没有 :g/pattern/command 这种单行命令,而是将匹配和操作拆解为两个步骤,通过组合实现相同甚至更强的功能。
occur + edit-list(最接近 Vim)这是最像 global 的工作流,先筛选出目标行,再批量编辑。
;; 1. 查找所有匹配行(类似 :g/pattern/p)
M-x occur RET foo RET
;; 2. 在 *Occur* 缓冲区中,按 e 进入编辑模式
;; 3. 修改任意行的内容,然后按 C-c C-c 提交,所有原文件对应行会同步更新
query-replace-regexp(交互式替换)这是 Emacs 版的“全局正则替换”,比 Vim 的 :%s 交互性更强。
;; 执行全局正则替换(类似 :%s/foo/bar/gc)
M-x query-replace-regexp RET foo RET bar RET
;; 交互选项:
;; y : 替换当前匹配
;; n : 跳过
;; ! : 替换所有剩余匹配(强制全局)
;; ^ : 回退到上一个匹配
mark + command(区域操作)如果你需要先“选中所有匹配”,再执行复杂操作(如缩进、注释),可以使用:
;; 1. 标记所有匹配行
M-x mark-matches RET foo RET
;; 2. 此时所有匹配行已被标记(region active)
;; 3. 执行任意命令,如批量缩进、注释等
M-x comment-dwim
global vs Emacs 实现Vim :global 场景 |
Emacs 等效操作 |
|---|---|
:g/foo/d |
M-x flush-lines RET foo |
:g/foo/s/bar/baz/ |
M-x occur → 编辑 → 提交 |
:g/foo/normal >> |
mark-matches → indent-rigidly |
:g!/foo/d |
M-x keep-lines RET foo |
注:Emacs 的
flush-lines和keep-lines是专门用于“删除匹配/不匹配行”的命令,比 Vim 的:g更语义化。
如果你确实怀念 Vim 的语法,可以用 Elisp 自定义一个 global 命令:
(defun my/global (regexp cmd)
"模拟 Vim 的 :global 命令"
(save-excursion
(goto-char (point-min))
(while (re-search-forward regexp nil t)
(let ((start (line-beginning-position))
(end (line-end-position)))
(funcall cmd start end)))))
;; 使用示例:删除所有含 "TODO" 的行
(my/global "TODO" (lambda (s e) (delete-region s e)))
总结:
Emacs 通过 occur(预览编辑)、query-replace-regexp(交互替换)和 mark-matches(标记操作)这三驾马车,覆盖了 Vim global 的所有场景,且安全性(可预览)和交互性更强。