vi 命令详解与参数实测(Ubuntu 22.04)
vi/vim 逐参数实测详解:-es 批处理与 -c 命令、+行号与 +/pattern 定位、-R/-M 只读、-o/-O 分屏、-d 差异、-u NONE、-b 二进制,插入族与 dd/p/u、可视模式、:%s 范围替换、:g 删除、寄存器、宏与 . 重复、swap 恢复 vim -r、viminfo,全部命令与回显在 Ubuntu 22.04(VIM 8.2)实测通过。
vi 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 vi 的启动参数与编辑键位——批处理 Ex 模式、定位打开、只读与分屏、插入族与移动、替换与全局命令、寄存器/宏/重复、swap 恢复,全部命令、按键序列与回显已在 Ubuntu 22.04(VIM 8.2)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
vi实际指向:/usr/bin/vi -> /etc/alternatives/vi -> /usr/bin/vim.basic(update-alternatives 管理,vim同指 vim.basic)- 版本:
VIM - Vi IMproved 8.2,Huge version without GUI(vi --version实测) - 实测工作目录:
/root/videmo/ - 批处理方法:
vim -es从标准输入读取 Ex 命令;交互方法:80x24 伪终端逐键驱动
vi --version 关键回显:
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)
Modified by team+vim@tracker.debian.org
Compiled by team+vim@tracker.debian.org
Huge version without GUI. Features included (+) or not (-):
vi --help 开头给出四种用法:
Usage: vim [arguments] [file ..] edit specified file(s)
or: vim [arguments] - read text from stdin
or: vim [arguments] -t tag edit file where tag is defined
or: vim [arguments] -q [errorfile] edit file with first error
二、命令概览
2.1 启动参数速查(均已实测)
| 参数 | 实测效果 |
|---|---|
-c <cmd> | 启动后执行 Ex 命令,可多次叠加(实测 -c '%s/^l/L/' -c 'wq') |
+<n> | 打开时光标定位到第 n 行(实测 +3 光标行=3) |
+/<pat> | 定位到首个匹配行(实测 +'/middle' 光标行=2) |
-e | Ex 模式;-es 静默批处理(脚本编辑标准姿势) |
-R | readonly 模式:修改允许,:w 需 :w! 强制(实测 wq! 可覆盖) |
-M | 彻底禁止修改(实测 wq! 也写不进,文件不变) |
-o n... | 水平分屏(实测两文件 winnr('$') 返回 2) |
-O n... | 垂直分屏(同上) |
-d | diff 模式(实测 &diff 返回 1) |
-u NONE | 跳过所有配置(实测 &compatible 返回 1) |
-b | 二进制模式(实测 &binary 返回 1) |
-r [file] | 列出可恢复的 swap 或从 swap 恢复(实测输出 Recovery completed) |
-V[n] | 冗长启动日志(排障用) |
-- | 之后的参数都当文件名 |
2.2 高频键位速查(均已实测)
| 键 | 模式 | 效果 |
|---|---|---|
i / a / o / I / A / O | 普通→插入 | 光标前/后插入、下插新行、行首/行尾插入、上插新行 |
Esc | 插入→普通 | 结束插入 |
dd / p / u / Ctrl-r | 普通 | 剪切行/粘贴/撤销/重做 |
v / V / Ctrl-v | 普通→可视 | 字符/行/块选择,d 删除选区 |
gg / G / 0 / $ / w / b | 普通 | 文件首/尾、行首/尾、词移动 |
/pat、?pat、n、N | 普通 | 向下/向上搜索、下一/上一匹配 |
: 视图 | 普通→命令行 | Ex 命令(:w、:q、:s、:g、:set…) |
ZZ / ZQ | 普通 | 保存退出 / 不保存退出 |
. | 普通 | 重复上一次修改 |
三、参数逐项实测
3.1 -es 批处理:脚本化编辑的事实标准
$ printf 'alpha beta gamma\none two three\nalpha again\n' > demo.txt
$ vim -es demo.txt <<'EOF'
:%s/alpha/ALPHA/g
:wq
EOF
$ cat demo.txt
ALPHA beta gamma
one two three
ALPHA again
从 stdin 读 Ex 命令、静默执行、:wq 落盘,退出码 0。把结果打到 stdout 用 :%p:
$ vim -es demo.txt <<'EOF'
:%p
:q!
EOF
ALPHA beta gamma
one two three
ALPHA again
实测坑点:vim -es 没有重定向 stdin 时(如写在 shell 脚本里裸调用),vim 会消费 shell 尚未读到的脚本行当作 Ex 命令,导致脚本"跳行"执行。务必用 heredoc 或 </dev/null(仅靠 -c 传命令时)。
3.2 -c 叠加命令
$ vim -es -c '%s/^l/L/' -c 'wq' nums.txt </dev/null
$ cat nums.txt
L1
L2
L3
L4
多个 -c 按顺序执行,最后一个负责写盘退出。
3.3 +n 与 +/pattern 定位打开
用批处理探针验证光标落点(line(".") 输出当前行号):
$ vim -es +3 -c 'redir >> /dev/stdout | echo line(".") | redir END' -c 'q!' pos.txt </dev/null
3
$ vim -es +'/middle' -c '...' -c 'q!' pat.txt </dev/null
2
交互等价形式:vim +3 file 打开后状态栏显示 3,1。实测打开 4L, 12B 文件时底部回显 "~/videmo/nums.txt" 4L, 12B 与 3,1All。
3.4 -R 只读与 -M 禁改(语义差异实测)
-R 只设置 readonly 标志,修改允许,强制写仍可成功(实测复现):
$ vim -es -R -c 'normal! Gooops' -c 'wq!' -c 'q!' nums.txt </dev/null
$ cat nums.txt # oops 行被写入了
-M 则真正禁止修改,normal! Gix 后 wq! 也写不进(实测文件保持原样)。需要"确保不改"用 -M;"-R" 只是提醒。
3.5 -o / -O 分屏与 -d 差异
$ vim -es -o nums.txt pat.txt -c 'redir >> /dev/stdout | echo winnr("$") | redir END' -c 'qall!' </dev/null
2 ← 两个水平窗口
$ vim -es -O nums.txt pat.txt -c '...' -c 'qall!' </dev/null
2 ← 两个垂直窗口
$ vim -es -d d1.txt d2.txt -c 'redir >> /dev/stdout | echo &diff | redir END' -c 'qall!' </dev/null
1 ← diff 已开启
3.6 -u NONE 与 -b
$ vim -es -u NONE -c 'redir >> /dev/stdout | echo &compatible | redir END' -c 'q!' </dev/null
1 ← vi 兼容模式被启用(配置未加载的标志)
$ vim -es -b -c 'redir >> /dev/stdout | echo &binary | redir END' -c 'q!' bin.dat </dev/null
1 ← 二进制模式
3.7 插入族:i a o A
$ vim ins.txt # 空文件
(按 i,输入 "vim inserted",Esc,:wq 回车)
"ins.txt" [New] 1L, 13B written
继续编辑实测 o(下插新行)与 A(行尾追加):
(o 输入 "second line",Esc;A 输入 "x tail",Esc;:wq)
"ins.txt" 2L, 31B written
$ cat ins.txt
vim inserted
second linex tail
A 从行尾继续插入,无空格拼接,符合语义。
3.8 dd 剪切、p 粘贴、u 撤销
对 2 行文件执行 dd(剪第一行)→ p(贴回)→ u(撤销)→ u(再撤销)后 :wq:
"ins.txt" 2L, 31B written ← 内容与行数不变
两次 undo 抵消 dd 与 p,往返无损。
3.9 ZZ 与 ZQ 退出键
(修改后按 ZZ)
"ins.txt" 3L, 39B written ← 保存并退出
(再修改后按 ZQ)
$ cat ins.txt ← 文件未变(ZQ 放弃修改退出)
3.10 :q 的 E37 与 :q!
修改缓冲后 :q 回车,实测报错:
E37: No write since last change (add ! to override)
:q! 强制退出,修改丢弃。
3.11 搜索 /pattern 与 n / N
(/second 回车)
/secondsecond ← 命令行回显与命中行
(状态栏变为 2,1All,光标跳至第 2 行)
n 跳下一个匹配、N 上一个。搜索历史会写入 ~/.viminfo(实测生成 876 字节)。
3.12 :%s 替换与标志
$ printf 'cat dog cat\nCAT Cat\n' > rep.txt
$ vim -es -c '%s/cat/fish/gI' -c 'wq' rep.txt </dev/null
$ cat rep.txt
fish dog fish
FISH Fish
g 全行所有匹配、I 忽略大小写。范围替换实测:
$ vim -es -c '1,2s/x/y/' -c '$s/x/z/' -c 'wq' rng.txt </dev/null
$ cat rng.txt
y
y
z
3.13 :g 全局命令删除
$ vim -es -c 'g/^DROP/d' -c 'wq' gdel.txt </dev/null
$ cat gdel.txt
keep1
keep2
3.14 可视模式 V 行选择删除
交互实测:V 选中整行 → d 删除选区 → :wq:
"rep.txt" 1L, 8B written
CAT Cat
3 行变 1 行。v 字符选择、Ctrl-v 块选择同流程。
3.15 具名寄存器
$ vim -es -c 'normal! "ayydd"ap' -c 'wq' reg.txt </dev/null
$ cat reg.txt
r1
r2
r3
"ayy 复制当前行进寄存器 a,dd 剪切,"ap 从 a 粘贴——往返无损。
3.16 . 重复与宏录制 q
$ vim -es -c 'execute "normal! A-x\<Esc>."' -c 'wq' dot.txt </dev/null
$ cat dot.txt
one-x-x ← A-x 追加 "-x",. 再重复一次
$ vim -es -c 'execute "normal! qaA!\<Esc>j0q@a"' -c 'wq' macro.txt </dev/null
$ cat macro.txt
m1
m2!
宏 qa 开始录制到寄存器 a,A! 行尾加 !,j0 下移行首,q 结束,@a 在第二行回放。实测要点:批处理中必须经 execute "normal! ...\<Esc>." 注入 Esc,裸 normal! A-x. 会把 . 当插入文本(得到 one-x.)。
3.17 数字前缀命令在批处理中的差异(实测发现)
$ vim -es -c 'normal! 3dd' -c 'wq' num.txt </dev/null # 文件 5 行不变!
$ vim -es -c 'execute "normal! 3dd"' -c 'wq' num.txt # 同样不生效
$ vim -es -c 'normal! 2G3dd' -c 'wq' num.txt </dev/null # 生效:剩 a、e
$ vim -es -c '1,3d' -c 'wq' num.txt </dev/null # Ex 范围命令生效
-es 静默批处理下,:normal! 的行首数字前缀(3dd)不执行;数字前带定位动作(2G3dd)或改用 Ex 范围命令(1,3d)则正常。脚本编辑时优先用 Ex 范围命令。
3.18 其他实用命令
$ vim -es -c 'normal! gUiw' -c 'wq' up.txt </dev/null # LOWER case words
$ vim -es -c 'set shiftwidth=4' -c 'normal! >>' -c 'wq' ind.txt </dev/null
$ cat -A ind.txt
x$
y$ ← 缩进 4 空格
$ vim -es -c 'set number' -c 'set number?' -c 'q!' pn.txt </dev/null
number ← 选项回显查询
:w 目标文件 另存不改变当前文件(实测源文件 s1 未变、副本 S1 已替换)。
3.19 swap 文件与 vim -r 恢复
交互会话未保存退出(或被强杀)会残留 swap 文件 .ins.txt.swp;再次打开同名文件时 vim 提示 Found a swap file ... 及 E325: ATTENTION。恢复实测:
$ vim -r /root/videmo/ins.txt
Using swap file "~/videmo/.ins.txt.swo"
Original file "~/videmo/ins.txt"
Recovery completed. You should check if everything is OK.
恢复后用 :wq 保存,删除残留 swap。vim -r 不带文件名时列出全部可恢复会话。
3.20 选项与运行时路径
-es 下 $VIMRUNTIME 指向 /usr/share/vim/vim82(Ubuntu 打包路径);:set opt? 形式可回显任意选项当前值(如 set number? 输出 number)。
四、实战示例
4.1 脚本内批量替换(标准范式)
$ vim -es -c '%s/old/new/g' -c 'x' file.conf </dev/null
-es + -c + </dev/null + :x(保存退出)是最稳的脚本编辑四件套。
4.2 定位修改:打开即到目标行
$ vim +42 nginx.conf # 直接跳到第 42 行
$ vim +/'server_name' nginx.conf
4.3 双文件差异比对
$ vim -d a.conf b.conf # diff 模式,差异行高亮折叠
4.4 崩溃后找回未保存内容
$ vim -r # 列出可恢复会话
$ vim -r important.txt # 恢复 → 检查 → :wq
五、注意事项与常见问题
vim -es必须管控 stdin:裸调用会吃掉 shell 脚本后续行(实测复现);heredoc 或</dev/null二选一。- 批处理中
:normal!数字前缀失效:3dd不执行,用2G3dd或 Ex 范围命令1,3d。 -R不是写保护::w!仍可覆盖;真正禁改用-M。ZZ与:wq等价、ZQ与:q!等价,但ZZ在 readonly 缓冲也会提示。- 宏/
.的 Esc:normal!参数里写\<Esc>并用execute包裹,否则后续字符全部进插入缓冲。 - swap 冲突提示 E325:确认无并发编辑后选
(O)pen Read-Only或删除.swp;恢复用vim -r。 viminfo记录搜索/命令/寄存器历史:~/.viminfo由会话正常退出时更新,换机不迁移。-b二进制模式不会把末尾缺换行当异常,noeol相关报错可忽略。:%p是批处理取回文本的手段;redir >> /dev/stdout | echo expr | redir END可取表达式值。- Ubuntu 的 vi 走 alternatives:
update-alternatives --display vi可查看当前实现(vim.basic)。
六、参考链接
- Vim 官方文档:https://www.vim.org/docs.php
- Vim 中文手册:https://vimcdoc.sourceforge.net/
man vim、vimtutor(交互教程,Ubuntu 随 vim 包提供)- vim 源码仓库:https://github.com/vim/vim
站内相关:nano 篇