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)实测通过。

Linux命令vimLinux系统UbuntuUbuntu 22.04命令行2026/10/17

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)
-eEx 模式;-es 静默批处理(脚本编辑标准姿势)
-Rreadonly 模式:修改允许,:w 需 :w! 强制(实测 wq! 可覆盖)
-M彻底禁止修改(实测 wq! 也写不进,文件不变)
-o n...水平分屏(实测两文件 winnr('$') 返回 2)
-O n...垂直分屏(同上)
-ddiff 模式(实测 &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

五、注意事项与常见问题

  1. vim -es 必须管控 stdin:裸调用会吃掉 shell 脚本后续行(实测复现);heredoc 或 </dev/null 二选一。
  2. 批处理中 :normal! 数字前缀失效:3dd 不执行,用 2G3dd 或 Ex 范围命令 1,3d。
  3. -R 不是写保护::w! 仍可覆盖;真正禁改用 -M。
  4. ZZ 与 :wq 等价、ZQ 与 :q! 等价,但 ZZ 在 readonly 缓冲也会提示。
  5. 宏/. 的 Esc:normal! 参数里写 \<Esc> 并用 execute 包裹,否则后续字符全部进插入缓冲。
  6. swap 冲突提示 E325:确认无并发编辑后选 (O)pen Read-Only 或删除 .swp;恢复用 vim -r。
  7. viminfo 记录搜索/命令/寄存器历史:~/.viminfo 由会话正常退出时更新,换机不迁移。
  8. -b 二进制模式不会把末尾缺换行当异常,noeol 相关报错可忽略。
  9. :%p 是批处理取回文本的手段;redir >> /dev/stdout | echo expr | redir END 可取表达式值。
  10. Ubuntu 的 vi 走 alternatives:update-alternatives --display vi 可查看当前实现(vim.basic)。

六、参考链接

站内相关:nano 篇