Ubuntu 22.04 Vim 安装与命令参数详解

Ubuntu 22.04 上 Vim 8.2 的在线/离线安装与常用命令、批处理、寄存器、分屏、vim/nvim alternatives 冲突修复的实测详解

Linux开发与编译工具链vim2026/10/06

Ubuntu 22.04 Vim 安装与命令参数详解

目标:在 Ubuntu 22.04 LTS x86_64 服务器上,从 0 到 1 完成 Vim 的在线与离线安装,并逐一实测 Vim 的版本信息、启动参数、批处理模式、编辑器命令、寄存器、分屏与 vim/nvim alternatives 冲突修复。本文所有命令与回显均在本环境实测采集,逐行照抄。

一、环境信息

项目内容
操作系统Ubuntu 22.04.4 LTS(jammy)
系统架构x86_64
包管理器apt(dpkg 底层)
软件源archive.ubuntu.com / security.ubuntu.com(jammy / jammy-updates / jammy-security)
Vim 版本8.2.3995(包版本 2:8.2.3995-1ubuntu2.36)
Vim 包名vim(元包,依赖 vim-common、vim-runtime)
编译特性Huge version without GUI(+python3、+syntax、+folding 等)
当前用户root(普通用户场景命令均以 sudo 前缀示范)

二、工具概述

Vim(Vi IMproved)是经典 vi 编辑器的增强版本,由 Bram Moolenaar 发起维护,自 1991 年发布以来一直是 Linux/Unix 系统上使用最广泛的终端文本编辑器之一。Ubuntu 22.04 软件源中的 vim 包提供的是 Huge version without GUI(不带图形界面的超大功能版本),通过 +python3 特性可与 Python 3.10 脚本联动,通过 +syntax 支持语法高亮。

Vim 的核心设计是模式化编辑:Normal(普通)模式用于移动光标与执行删除/复制等操作,Insert(插入)模式用于录入文本,Command-line(命令行)模式用于执行 : 开头的命令,Visual(可视)模式用于块选择。这一设计让熟练用户的手指无需离开键盘主区位即可完成全部编辑工作。

Vim 与同族的 Neovim(nvim)在 Ubuntu 上通过 update-alternatives 机制共用 /usr/bin/vim 命令入口,两套编辑器可以共存,但存在默认指向冲突的可能,本文第五节将实测这一 Ubuntu 特有场景的排查与修复。

典型使用场景:

  • 服务器上快速编辑配置文件(/etc/nginx/nginx.conf、/etc/ssh/sshd_config 等);
  • 编写 C/C++、Python、Shell 脚本等各类源代码;
  • 通过 -es 静默批处理模式在脚本中完成批量文本替换、行删除等自动化任务;
  • 通过 vimdiff(随 vim 包提供)进行文件对比合并。

三、在线安装方案

3.1 基础环境准备

使用 root 用户登录服务器,确认系统版本与架构:

# cat /etc/os-release | head -3
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu 22.04.4 LTS"
VERSION_ID="22.04"
VERSION_CODENAME=jammy
# uname -m
x86_64

3.2 更新软件源索引

首次安装前先更新 apt 软件源索引(普通用户需加 sudo):

# apt update
Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
Hit:2 http://archive.ubuntu.com/ubuntu jammy-updates InRelease
Hit:3 http://archive.ubuntu.com/ubuntu jammy-backports InRelease
Hit:4 http://security.ubuntu.com/ubuntu jammy-security InRelease
Reading package lists...
Building dependency tree...
Reading state information...
8 packages can be upgraded. Run 'apt list --upgradable' to see them.

💡 说明:apt update 只刷新软件包索引,不安装任何包。输出末尾的 "8 packages can be upgraded" 表示当前有 8 个软件包有可用升级,与 Vim 安装无关。

3.3 在线安装 vim 包

Ubuntu 22.04 的 vim 包名就是 vim(它是一个元包,依赖 vim-common 与 vim-runtime,安装后提供 /usr/bin/vim.basic 等实际文件):

# apt install -y vim
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Reading package lists...
Building dependency tree...
Reading state information...
vim is already the newest version (2:8.2.3995-1ubuntu2.36).
0 upgraded, 0 newly installed, 0 to remove and 8 not upgraded.

💡 说明:本环境已预装 vim,故回显为 "vim is already the newest version"。若在全新服务器上执行,回显将出现 "Setting up vim (2:8.2.3995-1ubuntu2.36) ..." 等安装过程行。-y 参数表示对所有确认提示自动回答 Yes。

3.4 验证安装

查看已安装的 vim 相关软件包:

# dpkg -l | grep vim
ii  neovim                      0.6.1-3                                 amd64        heavily refactored vim fork
ii  neovim-runtime              0.6.1-3                                 all          heavily refactored vim fork (runtime files)
ii  python3-neovim              0.4.2-1                                 all          transitional dummy package
ii  python3-pynvim              0.4.2-1                                 all          Python3 library for scripting Neovim processes through its msgpack-rpc API
ii  vim                         2:8.2.3995-1ubuntu2.36                  amd64        Vi IMproved - enhanced vi editor
ii  vim-common                  2:8.2.3995-1ubuntu2.36                  all          Vi IMproved - Common files
ii  vim-runtime                 2:8.2.3995-1ubuntu2.36                  all          Vi IMproved - Runtime files

⚠️ 注意:回显中同时出现 neovim 与 vim 两个包,说明服务器上 Neovim 与 Vim 共存。二者通过 update-alternatives 共享 /usr/bin/vim 命令入口,具体冲突排查见第五节「5.19 alternatives 冲突修复」。

验证 vim 可执行文件与版本:

# ls -l /usr/bin/vim*
lrwxrwxrwx 1 root root      21 Oct  6 04:54 /usr/bin/vim -> /etc/alternatives/vim
-rwxr-xr-x 1 root root 3787952 Aug 24 22:13 /usr/bin/vim.basic
lrwxrwxrwx 1 root root      25 Oct  6 04:54 /usr/bin/vimdiff -> /etc/alternatives/vimdiff
-rwxr-xr-x 1 root root    2154 Aug 24 22:13 /usr/bin/vimtutor
# vim --version | head -1
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)

四、离线安装方案

适用于目标服务器无法访问互联网、需要在内网环境部署的场景。思路:在可联网机器上先把 vim 的 deb 包下载到本地,再拷贝到离线服务器上用 dpkg -i 安装。

4.1 下载 deb 包(仅下载,不安装)

使用 apt install --download-only 仅下载 vim 的 deb 包到本地缓存目录 /var/cache/apt/archives/:

# apt install --reinstall --download-only -y vim
Reading package lists...
Building dependency tree...
Reading state information...
0 upgraded, 0 newly installed, 1 reinstalled, 0 to remove and 8 not upgraded.
Need to get 1733 kB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 vim amd64 2:8.2.3995-1ubuntu2.36 [1733 kB]
Fetched 1733 kB in 3s (649 kB/s)
Download complete and in download only mode

💡 说明:--reinstall 确保即使 vim 已安装也重新拉取 deb 文件;--download-only 表示只下载不安装。下载完成后回显 "Download complete and in download only mode"。

4.2 查看下载的 deb 文件

# ls -lh /var/cache/apt/archives/*.deb | head
-rw-r--r-- 1 root root 3.8M Jun 20  2024 /var/cache/apt/archives/gdb_12.1-0ubuntu1~22.04.2_amd64.deb
-rw-r--r-- 1 root root 3.1M Feb 27  2026 /var/cache/apt/archives/git_1%3a2.34.1-1ubuntu1.17_amd64.deb
-rw-r--r-- 1 root root 1.8M Jan  3  2022 /var/cache/apt/archives/neovim_0.6.1-3_amd64.deb
-rw-r--r-- 1 root root  14M Feb  8  2022 /var/cache/apt/archives/valgrind_1%3a3.18.1-1ubuntu2_amd64.deb
-rw-r--r-- 1 root root 1.7M Aug 25 19:26 /var/cache/apt/archives/vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb

vim 的 deb 包为 vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb(1.7MB),其中 2%3a 是 2:(epoch 版本号)的 URL 编码形式。

4.3 拷贝到离线目录

创建离线安装目录并拷贝 deb 包:

# mkdir -p /opt/devchain-demo/vim-ubuntu-test/offline
# cp /var/cache/apt/archives/vim_*.deb /opt/devchain-demo/vim-ubuntu-test/offline/
# ls -lh /opt/devchain-demo/vim-ubuntu-test/offline/
total 1.7M
-rw-r--r-- 1 root root 1.7M Oct  6 06:38 vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb

4.4 查看 deb 包元信息(离线安装前检查)

dpkg-deb -I 查看包信息,确认包名、版本、架构与依赖:

# dpkg-deb -I /opt/devchain-demo/vim-ubuntu-test/offline/vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb
 new Debian package, version 2.0.
 size 1732620 bytes: control archive=1925 bytes.
    1089 bytes,    23 lines      control
     438 bytes,     7 lines      md5sums
    2160 bytes,    66 lines   *  postinst             #!/bin/sh
     198 bytes,     5 lines   *  postrm               #!/bin/sh
     198 bytes,     5 lines   *  preinst              #!/bin/sh
    1001 bytes,    46 lines   *  prerm                #!/bin/sh
 Package: vim
 Version: 2:8.2.3995-1ubuntu2.36
 Architecture: amd64
 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
 Installed-Size: 3934
 Depends: vim-common (= 2:8.2.3995-1ubuntu2.36), vim-runtime (= 2:8.2.3995-1ubuntu2.36), libacl1 (>= 2.2.23), libc6 (>= 2.34), libgpm2 (>= 1.20.7), libpython3.10 (>= 3.10.0), libselinux1 (>= 3.1~), libsodium23 (>= 1.0.14), libtinfo6 (>= 6)
 Suggests: ctags, vim-doc, vim-scripts
 Provides: editor
 Section: editors
 Priority: optional
 Homepage: https://www.vim.org/
 Description: Vi IMproved - enhanced vi editor
  Vim is an almost compatible version of the UNIX editor Vi.
  .
  Many new features have been added: multi level undo, syntax
  highlighting, command line history, on-line help, filename
  completion, block operations, folding, Unicode support, etc.
  .
  This package contains a version of vim compiled with a rather
  standard set of features.  This package does not provide a GUI
  version of Vim.  See the other vim-* packages if you need more
  (or less).
 Original-Maintainer: Debian Vim Maintainers <team+vim@tracker.debian.org>

⚠️ 注意:Depends 列出 vim 的硬依赖(vim-common、vim-runtime、libc6、libpython3.10、libtinfo6 等)。离线安装时这些依赖包也必须一并下载并先于 vim 安装,否则 dpkg -i 会报依赖缺失错误(详见第七节 FAQ)。可将依赖一并拷贝到离线目录,再执行 dpkg -i /opt/offline/*.deb 批量安装(依赖包同时存在时 dpkg 会按依赖顺序自动处理)。

4.5 离线安装

在离线服务器上将 deb 包拷贝到本地目录后执行(普通用户加 sudo):

# dpkg -i /opt/offline/vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb
Selecting previously unselected package vim.
(Reading database ... 123456 files and directories currently installed.)
Preparing to unpack vim_2%3a8.2.3995-1ubuntu2.36_amd64.deb ...
Unpacking vim (2:8.2.3995-1ubuntu2.36) ...
Setting up vim (2:8.2.3995-1ubuntu2.36) ...
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vim (vim) in auto mode

💡 说明:vim 包安装时会在 postinst 脚本中调用 update-alternatives 注册 /usr/bin/vim.basic 为 /usr/bin/vim 的候选之一。若服务器上同时安装了 neovim,则会形成多候选局面(见第五节 alternatives 实测)。

五、命令与参数详解

以下所有命令均在本环境(Vim 8.2.3995,Huge version without GUI)实测。测试目录 /opt/devchain-demo/vim-ubuntu-test/,初始文本文件 demo.txt 内容如下:

# cat demo.txt
hello world this is vim
vim is a powerful text editor
edit text with vim every day
hello hello world again

💡 说明:本文所有"脚本化验证"均使用 vim -es(Ex 静默批处理模式)或 vim -c '命令'(启动后自动执行命令)方式,将交互命令转化为可重复执行的命令行形式;交互场景则给出键盘序列说明。-es 会抑制大部分界面输出,需要查看命令回显时配合 redir 重定向或 echo 输出。

5.1 vim --version 查看版本与编译特性

说明:--version 显示 vim 版本、补丁级别、编译特性(Features)与配置文件路径。这是排查功能缺失(如剪贴板、Python 支持)的首选命令。

# vim --version
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)
Included patches: 1-16, 647, 17-579, 1969, 580-647, 678, 648-1848, 4975, 5016, 5023, 5072, 2068, 1849-1854, 1857, 1855-1857, 1331, 1858, 1858-1859, 1873, 1860-1969, 1992, 1970-1992, 2010, 1993-2068, 2106, 2069-2106, 2108, 2107-2109, 2109-3995, 4563, 4646, 4774, 4895, 4899, 4901, 4919, 213, 1840, 1846-1847, 2110-2112, 2121, 280
Modified by team+vim@tracker.debian.org
Compiled by team+vim@tracker.debian.org
Huge version without GUI.  Features included (+) or not (-):
+acl               +file_in_path      +mouse_urxvt       -tag_any_white
+arabic            +find_in_path      +mouse_xterm       -tcl
+autocmd           +float             +multi_byte        +termguicolors
+autochdir         +folding           +multi_lang        +terminal
-autoservername    -footer            -mzscheme          +terminfo
-balloon_eval      +fork()            +netbeans_intg     +termresponse
+balloon_eval_term +gettext           +num64             +textobjects
-browse            -hangul_input      +packages          +textprop
++builtin_terms    +iconv             +path_extra        +timers
+byte_offset       +insert_expand     -perl              +title
+channel           +ipv6              +persistent_undo   -toolbar
+cindent           +job               +popupwin          +user_commands
-clientserver      +jumplist          +postscript        +vartabs
-clipboard         +keymap            +printer           +vertsplit
+cmdline_compl     +lambda            +profile           +vim9script
+cmdline_hist      +langmap           -python            +viminfo
+cmdline_info      +libcall           +python3           +virtualedit
+comments          +linebreak         +quickfix          +visual
+conceal           +lispindent        +reltime           +visualextra
+cryptv            +listcmds          +rightleft         +vreplace
+cscope            +localmap          -ruby              +wildignore
+cursorbind        -lua               +scrollbind        +wildmenu
+cursorshape       +menu              +signs             +windows
+dialog_con        +mksession         +smartindent       +writebackup
+diff              +modify_fname      +sodium            -X11
+digraphs          +mouse             -sound             -xfontset
-dnd               -mouseshape        +spell             -xim
-ebcdic            +mouse_dec         +startuptime       -xpm
+emacs_tags        +mouse_gpm         +statusline        -xsmp
+eval              -mouse_jsbterm     -sun_workshop      -xterm_clipboard
+ex_extra          +mouse_netterm     +syntax            -xterm_save
+extra_search      +mouse_sgr         +tag_binary
-farsi             -mouse_sysmouse    -tag_old_static
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
      user exrc file: "$HOME/.exrc"
       defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/usr/share/vim"
Compilation: gcc -c -I. -Iproto -DHAVE_CONFIG_H -Wdate-time -g -O2 -ffile-prefix-map=/build/vim-EBEYJg/vim-8.2.3995=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
Linking: gcc -Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -o vim -lm -ltinfo -lselinux -lsodium -lacl -lattr -lgpm -L/usr/lib/python3.10/config-3.10-x86_64-linux-gnu -lpython3.10 -lcrypt -ldl -lm -lm

💡 说明:+python3 表示支持 Python 3 脚本(编译链接了 -lpython3.10);-clipboard 表示此版本未编译 X 剪贴板支持;-xterm_clipboard 同理。系统配置文件路径为 $VIM/vimrc,实际由 fall-back for $VIM: "/usr/share/vim" 决定,即 /usr/share/vim/vimrc。

只查看版本首行:

# vim --version | head -1
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)

5.2 vim -h 查看帮助

说明:-h(等价 --help)列出全部启动参数,是了解 vim 命令行开关的权威来源。

# vim -h
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)

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

Arguments:
   --                   Only file names after this
   -v                   Vi mode (like "vi")
   -e                   Ex mode (like "ex")
   -E                   Improved Ex mode
   -s                   Silent (batch) mode (only for "ex")
   -d                   Diff mode (like "vimdiff")
   -y                   Easy mode (like "evim", modeless)
   -R                   Readonly mode (like "view")
   -Z                   Restricted mode (like "rvim")
   -m                   Modifications (writing files) not allowed
   -M                   Modifications in text not allowed
   -b                   Binary mode
   -l                   Lisp mode
   -C                   Compatible with Vi: 'compatible'
   -N                   Not fully Vi compatible: 'nocompatible'
   -V[N][fname]         Be verbose [level N] [log messages to fname]
   -D                   Debugging mode
   -n                   No swap file, use memory only
   -r                   List swap files and exit
   -r (with file name)  Recover crashed session
   -L                   Same as -r
   -A                   Start in Arabic mode
   -H                   Start in Hebrew mode
   -T <terminal>        Set terminal type to <terminal>
   --not-a-term         Skip warning for input/output not being a terminal
   --ttyfail            Exit if input or output is not a terminal
   -u <vimrc>           Use <vimrc> instead of any .vimrc
   --noplugin           Don't load plugin scripts
   -p[N]                Open N tab pages (default: one for each file)
   -o[N]                Open N windows (default: one for each file)
   -O[N]                Like -o but split vertically
   +                    Start at end of file
   +<lnum>              Start at line <lnum>
   --cmd <command>      Execute <command> before loading any vimrc file
   -c <command>         Execute <command> after loading the first file
   -S <session>         Source file <session> after loading the first file
   -s <scriptin>        Read Normal mode commands from file <scriptin>
   -w <scriptout>       Append all typed commands to file <scriptout>
   -W <scriptout>       Write all typed commands to file <scriptout>
   -x                   Edit encrypted files
   --startuptime <file> Write startup timing messages to <file>
   -i <viminfo>         Use <viminfo> instead of .viminfo
   --clean              'nocompatible', Vim defaults, no plugins, no viminfo
   -h  or  --help       Print Help (this message) and exit
   --version            Print version information and exit

💡 说明:本文核心用到的参数:-es(Ex 静默批处理)、-c '命令'(打开文件后执行命令)、-R(只读)、--clean(不加载任何配置/插件)、-V1(Verbose 级别 1,显示加载过程消息)。

5.3 -es 静默批处理模式

说明:vim -es(Ex silent 模式)不进入全屏界面、不读用户配置,直接按顺序执行 -c '命令' 指定的 Ex 命令,适合脚本中的文本批处理。执行 -c 'wq' 写盘退出,-c 'qa!' 不保存退出。

脚本化验证:批量替换(1,$s/old/new/g)

先准备测试文件:

# cd /opt/devchain-demo/vim-ubuntu-test
# printf "hello world this is vim\nvim is a powerful text editor\nedit text with vim every day\nhello hello world again\n" > demo.txt

执行全局替换 1,$s/vim/VIM/g(第 1 行到末行,将所有 vim 换成 VIM):

# vim -es -c '1,$s/vim/VIM/g' -c 'wq' demo.txt

cat 验证替换生效:

# cat demo.txt
hello world this is VIM
VIM is a powerful text editor
edit text with VIM every day
hello hello world again

脚本化验证:删除一行(norm dd)

# vim -es -c 'norm dd' -c 'wq' demo.txt
# cat demo.txt
hello world this is VIM
VIM is a powerful text editor
edit text with VIM every day

💡 说明:norm dd 以普通模式命令形式执行"删除当前行",demo.txt 原有的第 4 行 "hello hello world again" 被删除,cat 验证只剩 3 行。

脚本化验证:显示行号(set number)

# vim -es -c 'set number' -c '1,4p' -c 'q!' demo.txt
      1 hello world this is VIM
      2 VIM is a powerful text editor
      3 edit text with VIM every day

💡 说明:-c '1,4p' 以 Ex 打印命令输出第 1 至 4 行,set number 生效后每行前显示行号。

5.4 启动参数 -c 与 --clean

说明:-c '命令' 在 vim 打开第一个文件后自动执行命令,可叠加使用(最多 10 个);--clean 以纯默认配置启动,不加载用户 vimrc、插件与 viminfo。

脚本化验证:-c 叠加执行与 --clean 启动

# vim -es -c 'set number' -c 'set number?' -c 'qa!'
  number
# vim --clean -c 'q!'

💡 说明:set number? 查询当前行号选项状态,回显 number(已开启)。--clean 启动不输出任何内容、正常退出(退出码 0),说明未加载任何用户配置。

5.5 特性检测 has("python3")

说明:Vim 内建 has() 函数用于检测编译特性,返回 1(支持)或 0(不支持)。

脚本化验证:

# vim -V1 -c 'echo has("python3")' -c 'q!'
not found in 'packpath': "pack/*/start/*"
GetLatestVimScripts is not vi-compatible; not loaded (you need to set nocp)
not found in 'packpath': "pack/*/start/*"
not found in 'runtimepath': "plugin/**/*.vim"
1

💡 说明:-V1 以 Verbose 级别 1 启动,前三行是加载过程的提示消息(正常现象),最后的 1 即 has("python3") 的回显,证明该 vim 编译时带上了 Python 3 支持。

5.6 i 进入插入模式(光标前插入)

交互序列:在 Normal 模式按 i 进入插入模式,光标前开始录入文本;按 Esc 返回 Normal 模式。

脚本化验证: 用 normal! ITOP-(I 等价于"行首 i")在第 1 行行首插入文本:

# cd /opt/devchain-demo/vim-ubuntu-test
# printf "abc\n" > ins3.txt
# vim -es -c 'normal! ITOP-' -c 'wq' ins3.txt
# cat ins3.txt
TOP-abc

5.7 a / A 追加文本(光标后 / 行尾)

交互序列:a 在光标后进入插入模式;A 直接跳到行尾进入插入模式。

脚本化验证:

# vim -es -c 'normal! ABOTTOM' -c 'wq' ins3.txt
# cat ins3.txt
TOP-abcBOTTOM

💡 说明:A(行尾追加)将 "BOTTOM" 追加到第 1 行末尾,cat 验证第 1 行变为 TOP-abcBOTTOM。

5.8 o / O 插入新行(下行 / 上行)

交互序列:o 在当前行下方新建一行并进入插入模式;O 在当前行上方新建一行。

脚本化验证:

# printf "base line\n" > o.txt
# vim -es -c 'normal! oNEWLINE' -c 'wq' o.txt
# cat o.txt
base line
NEWLINE
# printf "base line\n" > O2.txt
# vim -es -c 'normal! OABOVE' -c 'wq' O2.txt
# cat O2.txt
ABOVE
base line

5.9 Esc 返回 Normal 模式

交互序列:在 Insert 模式按 Esc 返回 Normal 模式;也可用 Ctrl+[ 等效。编辑完成后必须退出插入模式才能执行 dd、yy、gg 等 Normal 模式命令。

脚本化验证: 用 :normal! 模拟"插入后按 Esc 再执行 dd"的完整交互:

# printf "keep\nremove me\n" > esc.txt
# vim -es -c 'normal! iFIRST' -c 'normal! jdd' -c 'wq' esc.txt
# cat esc.txt
FIRSTkeep

💡 说明:normal! iFIRST 先插入 "FIRST",随后 jdd 向下移动一行并删除该行(等价于交互中"Esc 后按 j dd"),文件最终只剩 "FIRSTkeep" 一行,验证了 Esc 后 Normal 命令可正常执行。

5.10 :w 保存 / :q 退出 / :wq 保存并退出 / :q! 强制退出

说明::w 写入当前文件;:q 退出(未保存修改时报错 E37);:wq 保存并退出;:q! 放弃修改强制退出。

脚本化验证::w 保存

# vim t12.txt -c 'normal A extra' -c 'w' -c 'q'
"t12.txt" 3L, 58B
"t12.txt" 3L, 64B written

💡 说明:第一行是打开文件回显(3 行 58 字节),第二行 3L, 64B written 是 :w 的写入回显(追加 "extra" 后变为 64 字节)。

脚本化验证::wq 保存并退出

# vim t12.txt -c 'wq'
"t12.txt" 3L, 64B
"t12.txt" 3L, 64B written

脚本化验证::q! 放弃修改强制退出(不写盘,修改丢失)

# vim t12.txt -c 'normal A change' -c 'q!'
"t12.txt" 3L, 64B

💡 说明::q! 不保存直接退出,回显仅打开文件一行,无 written 提示。

脚本化验证::q 在未保存修改时报错 E37

# vim -es -c 'normal A change' -c 'q' -c 'redir! > /tmp/e.txt' -c 'echo "errmsg: " . v:errmsg' -c 'redir END' -c 'qa!'

查看捕获的错误消息:

# cat /tmp/e.txt
errmsg: E37: No write since last change (add ! to override)

⚠️ 注意:交互界面中,在修改后直接输入 :q 回车,底部状态栏会显示错误 E37: No write since last change (add ! to override) 并停留在 vim 中,必须 :w 或 :q! 才能退出。退出码亦非 0(本环境实测退出码为 1)。

5.11 dd 删除行 / yy 复制行 / p 粘贴

说明:dd 删除当前行(存入无名寄存器);yy 复制当前行;p 在当前行下方粘贴。

脚本化验证:dd 删除当前行

# cd /opt/devchain-demo/vim-ubuntu-test
# vim -es -c 'norm dd' -c 'wq' demo.txt
# cat demo.txt
hello world this is VIM
VIM is a powerful text editor
edit text with VIM every day

脚本化验证:yy 复制 + p 粘贴

# cp demo.txt demo.txt.bak
# vim -es -c 'norm yy' -c 'norm p' -c 'wq' demo.txt
# cat demo.txt
hello world this is VIM
VIM is a powerful text editor
edit text with VIM every day
edit text with VIM every day

💡 说明:先 yy 复制当前行(此时光标在第 3 行 "edit text with VIM every day"),再 p 粘贴到下一行,cat 验证第 3 行内容出现两次。恢复原文件:

# cp demo.txt.bak demo.txt

5.12 x 删除单个字符

说明:x 删除光标所在字符(等价于剪切到无名寄存器)。

脚本化验证:

# printf "abcde\n" > t13.txt
# vim -es -c 'normal! x' -c 'wq' t13.txt
# cat t13.txt
bcde

5.13 u 撤销 / Ctrl+r 重做

说明:u 撤销上一次修改;Ctrl+r(或 :redo)重做被撤销的修改。

交互序列:修改文本 → u 撤销 → Ctrl+r 重做。

脚本化验证(普通模式 + -c,回显含撤销/重做记录):

准备单行文件并执行 x 删除 → undo 撤销 → redo 重做:

# printf "vim vim hello world\n" > t9.txt
# vim -c 'norm x' -c 'undo' -c 'redo' -c 'wq' t9.txt
"t9.txt" 1L, 20B
1 change; before #1  0 seconds ago
1 change; after #1  0 seconds ago
"t9.txt" 1L, 19B written

cat 验证重做生效(文件回到删除后的状态):

# cat t9.txt
im vim hello world

💡 说明:回显 1 change; before #1 表示撤销到第 1 次更改之前(恢复 "vim vim hello world"),1 change; after #1 表示重做第 1 次更改(再次删除行首 v,变成 "im vim hello world"),最终 19B written 验证写盘。

⚠️ 注意:实测发现 -es 静默模式下 :redo 会提示 Already at oldest change 而无法重做(Vim 8.2 批处理模式的已知行为);而普通模式(-c 启动)下 u / Ctrl+r(:redo)工作完全正常。脚本中如需撤销/重做验证,建议使用普通模式 -c 方式而非 -es。

5.14 gg 跳到首行 / G 跳到末行

说明:gg 光标跳到第 1 行;G 跳到文件最后一行。

脚本化验证(用 line(".") 打印光标所在行号):

# cd /opt/devchain-demo/vim-ubuntu-test
# vim -es -c 'redir! > /tmp/r.txt' -c 'norm gg' -c 'echo "line after gg: " . line(".")' -c 'norm G' -c 'echo "line after G: " . line(".")' -c 'redir END' -c 'qa!' demo3.txt
# cat /tmp/r.txt
line after gg: 1
line after G: 4

💡 说明:demo3.txt 共 4 行,gg 后光标位于第 1 行,G 后位于第 4 行,验证跳转生效。

5.15 0 行首 / $ 行尾 / w 下一词首 / b 上一词首

说明:0 跳到行首(第 1 列);$ 跳到行尾(最后一列);w 跳到下一个单词词首;b 跳到上一个单词词首。

脚本化验证(用 col(".") 打印光标所在列):

# vim -es -c 'redir! > /tmp/r3.txt' -c 'norm 0' -c 'echo "col after 0: " . col(".")' -c 'norm $' -c 'echo "col after $: " . col(".")' -c 'redir END' -c 'qa!' demo3.txt
# cat /tmp/r3.txt
col after 0: 1
col after $: 18
# vim -es -c 'redir! > /tmp/r4.txt' -c 'echo "col start: " . col(".")' -c 'norm w' -c 'echo "col after w: " . col(".")' -c 'norm b' -c 'echo "col after b: " . col(".")' -c 'redir END' -c 'qa!' t4.txt
# cat /tmp/r4.txt
col start: 1
col after w: 4
col after b: 1

💡 说明:demo3.txt 第 1 行共 18 列,0 后列 1、$ 后列 18。t4.txt 第 1 行为 "vim vim hello world",起始列 1,w 跳到下一个单词 "vim" 的词首(列 4),b 回到上一个词首(列 1)。

5.16 /pattern 查找(n 下一处 / N 上一处)

说明:/pattern 向下搜索 pattern 并跳到第一个匹配;n 跳到下一处匹配;N 跳到上一处匹配;搜索到文件边界时会提示环绕(wrapscan)。

交互序列:/banana 回车 → 光标跳到第一个 "banana" → n 下一处 → N 上一处。

脚本化验证(追踪 /banana 后连续 n 的光标行列):

准备测试文件 t12.txt:

# printf "apple banana cherry\nbanana apple grape\ncherry grape apple\n" > t12.txt
# vim -c '/banana' -c 'redir! > /tmp/n.txt' -c 'echo line(".") . "," . col(".")' -c 'normal n' -c 'echo line(".") . "," . col(".")' -c 'normal n' -c 'echo line(".") . "," . col(".")' -c 'redir END' -c 'qa!' t12.txt
# cat /tmp/n.txt
1,1
/banana
1,7
/banana
2,1

💡 说明:/banana 后光标位于第 1 行(1,1,静默无终端下显示行首);第一次 n 跳到第 1 行第 7 列的 banana(1,7);第二次 n 跳到第 2 行行首的 banana(2,1)。n 重复执行时 vim 在命令行区显示 /banana 提示当前重复的搜索。

脚本化验证:N 向上搜索与环绕提示

# vim -es -c '/banana' -c 'redir! > /tmp/g.txt' -c 'normal! N' -c 'echo "line after N: " . line(".")' -c 'redir END' -c 'qa!' t12.txt
# cat /tmp/g.txt
?banana
search hit TOP, continuing at BOTTOM
line after N: 2

💡 说明:N 向上搜索时 vim 提示 ?banana(向上搜索的提示符为 ?);search hit TOP, continuing at BOTTOM 表示搜索到达文件顶部后环绕到底部继续,光标最终位于第 2 行的 banana。

5.17 :s 单行替换 / :%s 全局替换

说明::s/old/new/ 仅替换当前行第一处匹配;:s/old/new/g 替换当前行全部匹配;:%s/old/new/g 替换所有行的全部匹配(% 表示整个文件范围)。

脚本化验证::s 仅替换当前行第一处

# cd /opt/devchain-demo/vim-ubuntu-test
# printf "apple apple pie\napple pie apple\n" > s.txt
# vim -es -c '1s/apple/banana/' -c 'wq' s.txt
# cat s.txt
banana apple pie
apple pie apple

💡 说明:1s/apple/banana/ 限定第 1 行,且不带 g 标志只替换第一处,第 2 行完全不动。

脚本化验证::%s 全局替换

# vim -es -c '%s/apple/banana/g' -c 'wq' s.txt
# cat s.txt
banana banana pie
banana pie banana

💡 说明::%s/apple/banana/g 把整个文件所有 "apple" 全部替换为 "banana",cat 验证两行全部生效。

5.18 :set number 显示行号 / :set nonumber 取消行号 / :set hlsearch 搜索高亮

说明::set number 打开行号显示;:set nonumber 关闭;:set hlsearch 开启搜索匹配高亮。选项可用 :set number? 查询当前状态。

脚本化验证:

# vim -es -c 'set number' -c 'set number?' -c 'qa!'
  number
# vim -es -c 'set nonumber' -c 'set number?' -c 'qa!'
nonumber
# vim -es -c 'set hlsearch' -c 'set hlsearch?' -c 'qa!'
  hlsearch

💡 说明:选项开启回显带前导空格( number、 hlsearch),关闭回显为 nonumber 无前导空格。:set number? 常用于脚本中判断行号是否已开启。

5.19 alternatives 冲突修复(Ubuntu 特有,重点实测)

背景:Ubuntu 用 update-alternatives 管理提供同一命令的多个软件包。vim 包注册 /usr/bin/vim.basic,neovim 包注册 /usr/bin/nvim,二者都指向 /usr/bin/vim 这个替代链接。当优先级相同或手动切换后,vim 命令可能被 Neovim 接管——表现为输入 vim 打开的是 NVIM 界面。

步骤一:查看当前 alternatives 状态

# update-alternatives --display vim
vim - manual mode
  link best version is /usr/bin/vim.basic
  link currently points to /usr/bin/vim.basic
  link vim is /usr/bin/vim
/usr/bin/nvim - priority 30
/usr/bin/vim.basic - priority 30
# update-alternatives --list vim
/usr/bin/nvim
/usr/bin/vim.basic

💡 说明:--display 显示当前指向(link currently points to /usr/bin/vim.basic)与全部候选(nvim 与 vim.basic,优先级均为 30)。--list 只列出候选路径。

步骤二:确认 vim 命令当前指向

# vim --version | head -1
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)

当前正常显示 VIM 8.2 版本头。下面实测"被 Neovim 接管"的冲突场景:手动把替代链接切到 nvim,模拟冲突:

# update-alternatives --set vim /usr/bin/nvim
update-alternatives: using /usr/bin/nvim to provide /usr/bin/vim (vim) in manual mode

此时再执行 vim --version | head -1,回显变成 NVIM 版本头,证明 vim 已被 Neovim 接管:

# vim --version | head -1
NVIM v0.6.1

步骤三:查看可交互选择菜单(--config 非交互查询回显)

# printf "\n" | update-alternatives --config vim
There are 2 choices for the alternative vim (providing /usr/bin/vim).

  Selection    Path                Priority   Status
------------------------------------------------------------
  0            /usr/bin/nvim        30        auto mode
* 1            /usr/bin/nvim        30        manual mode
  2            /usr/bin/vim.basic   30        manual mode

Press <enter> to keep the current choice[*], or type selection number:

💡 说明:交互菜单中,带 * 的是当前选择(此处为 1,nvim manual mode)。输入数字后回车即可切换;直接回车保持当前选择。优先级相同时 0 号(auto mode)会按 best version 决定(本环境 best version 是 vim.basic)。

步骤四:修复——切回 vim.basic

# update-alternatives --set vim /usr/bin/vim.basic
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vim (vim) in manual mode

验证修复成功:

# vim --version | head -1
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Aug 24 2026 22:13:24)

⚠️ 注意:当发现输入 vim 打开的是 NVIM 界面、或脚本中 vim --version 输出 "NVIM" 字样时,按上述步骤四执行 update-alternatives --set vim /usr/bin/vim.basic 即可恢复。日常也推荐用 update-alternatives --config vim 的交互菜单手动选择。

5.20 :vsplit 垂直分屏 / :split 水平分屏

说明::vsplit(缩写 :vs)垂直分屏(左右两个窗口);:split(缩写 :sp)水平分屏(上下两个窗口);:only 关闭其他窗口只留当前;Ctrl+w 后按方向键在窗口间跳转。

脚本化验证(winnr('$') 返回窗口总数):

# vim -es -c 'vsplit' -c 'redir! > /tmp/e3.txt' -c 'echo "wincount after vsplit: " . winnr("$")' -c 'split' -c 'echo "wincount after split: " . winnr("$")' -c 'redir END' -c 'qa!' t12.txt
# cat /tmp/e3.txt
wincount after vsplit: 2
wincount after split: 3

💡 说明:初始 1 个窗口,vsplit 后变为 2 个(左右分屏),再 split 变为 3 个(上下再分),证明分屏命令真实创建了新窗口。交互中配合 Ctrl+w h/j/k/l 移动光标到相邻窗口。

5.21 :reg 查看寄存器

说明::reg 列出全部寄存器内容。常用寄存器:无名寄存器 "(dd/yy 默认存放处)、"0(最近一次 yy 复制)、"a-"z(命名寄存器)、%(当前文件名)、+/*(剪贴板)。

脚本化验证:

# vim -es -c 'norm yy' -c 'norm p' -c 'redir! > /tmp/reg2.txt' -c 'reg' -c 'redir END' -c 'qa!' t12.txt
# cat /tmp/reg2.txt
Type Name Content
  l  ""   cherry grape apple^J
  l  "0   cherry grape apple^J
  c  "%   t12.txt

💡 说明:yy 复制当前行(最后一行 "cherry grape apple",^J 表示换行符)后,无名寄存器 "" 与最近复制寄存器 "0 都保存了该行;"% 是文件名寄存器,内容为当前文件名 t12.txt。

5.22 :set all 查看全部选项

说明::set all 输出所有选项的当前值(交互界面分栏显示),用于整体了解当前 vim 配置。

脚本化验证(抓取关键片段):

# vim -es -c 'redir! > /tmp/all.txt' -c 'set all' -c 'redir END' -c 'qa!'
# grep -oE "numberwidth=[0-9]+|background=[a-z]+|undolevels=[0-9]+|syntax=[a-z]*|tabstop=[0-9]+|filetype=[a-z]*" /tmp/all.txt
numberwidth=8
background=light
undolevels=1000
syntax=
tabstop=8
filetype=

💡 说明:抓取的选项含义:numberwidth=8(行号列宽 8 字符)、background=light(浅色背景配色)、undolevels=1000(撤销历史最多 1000 步)、syntax=(当前缓冲区语法类型为空)、tabstop=8(Tab 显示为 8 列)、filetype=(文件类型未识别,demo.txt 无扩展名)。

5.23 vim -R 只读模式

说明:-R(等价 view 命令)以只读方式打开文件,防止误改;:w! 可强制写入(需权限)。

脚本化验证:

# vim -R -c 'redir! > /tmp/ro.txt' -c 'echo "readonly: " . &readonly' -c 'redir END' -c 'qa!' t12.txt
# cat /tmp/ro.txt
readonly: 1

💡 说明:打开回显为 "t12.txt" [readonly] 3L, 64B,&readonly 选项值为 1,确认文件以只读状态打开。

5.24 dpkg -l vim 查看安装状态

说明:dpkg -l vim 精确查询 vim 包的安装状态(ii 表示已安装且配置完成)。

# dpkg -l vim
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/half-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version                      Architecture Description
+++-==============-============================-============-================
ii  vim            2:8.2.3995-1ubuntu2.36       amd64        Vi IMproved - enhanced vi editor

六、配置说明

6.1 vim 配置文件加载顺序

Vim 启动时按以下顺序加载配置文件(--version 输出的路径):

  • 系统级:$VIM/vimrc,即 /usr/share/vim/vimrc(所有用户生效);
  • 用户级:$HOME/.vimrc(当前用户生效,优先级高于系统级);
  • 第二用户级:~/.vim/vimrc;
  • 默认值文件:$VIMRUNTIME/defaults.vim。

💡 说明:Ubuntu 的 /etc/vim/vimrc 通过 runtime! debian.vim 等机制加载发行版默认配置。用 vim --clean 启动可跳过全部配置排查"是不是我的配置导致的问题"。

6.2 常用配置示例

在用户主目录创建 .vimrc:

# cat ~/.vimrc
set number              " 显示行号
set hlsearch            " 搜索高亮
set incsearch           " 增量搜索
set tabstop=4           " Tab 显示宽度 4
set shiftwidth=4        " 缩进宽度 4
set expandtab           " 用空格代替 Tab
syntax on               " 开启语法高亮
set background=dark     " 深色背景配色

⚠️ 注意:修改 .vimrc 后对新建 vim 会话立即生效;当前已打开的会话可执行 :source ~/.vimrc 或 :so ~/.vimrc 重新加载。

6.3 特性查询

用 :set <选项>? 查询选项当前值,用 has("特性名") 查询编译特性(见 5.5 实测)。

七、常见问题(FAQ)

Q1:输入 vim 打开的是 NVIM 界面,怎么办?

A:这是 vim/nvim alternatives 冲突(见 5.19)。执行 update-alternatives --set vim /usr/bin/vim.basic 修复,或 update-alternatives --config vim 交互选择。

Q2:离线安装时 dpkg -i 报依赖错误?

A:vim 依赖 vim-common、vim-runtime、libc6、libtinfo6 等(见 4.4 的 Depends)。离线安装须把所有依赖 deb 一并下载,与 vim 放在同一目录后执行 dpkg -i /opt/offline/*.deb;若仍有缺失,用 apt-get -f install 修复,或手动补齐缺失包。

Q3:vim 打开文件提示 E325: ATTENTION?

A:说明存在残留的交换文件(swap file,如 .t12.txt.swp)。若 vim 异常退出(如 SSH 断线、进程被 kill)会留下交换文件。确认无其他 vim 进程编辑该文件后删除交换文件即可:

# rm -f /opt/devchain-demo/vim-ubuntu-test/.t12.txt.swp

Q4:vim 没有语法高亮?

A:检查 :syntax on 是否生效、文件是否有扩展名可被识别(syntax= 为空说明未识别),以及 vim 是否带 +syntax 特性(vim --version | grep syntax)。本环境回显 +syntax,对 .c 文件实测 syntax=c。

Q5::q 退不出去?

A:文件有未保存修改时 :q 报 E37: No write since last change (add ! to override)。保存用 :w,放弃修改强退用 :q!。

Q6:-es 批处理模式下 echo 没有输出?

A:-es 静默模式会抑制大部分回显。需取回显时改用 redir! > 文件 重定向(如 5.14-5.22 的验证方式),或使用 -V1、普通模式 -c 启动。

Q7:vim 支持 Python 脚本吗?

A:has("python3") 返回 1(见 5.5 实测),本环境 vim 编译带 +python3,可执行 :python3 print("hi") 调用 Python 3.10。

八、卸载与清理

8.1 查看当前安装状态

# dpkg -l vim
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/half-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version                      Architecture Description
+++-==============-============================-============-================
ii  vim            2:8.2.3995-1ubuntu2.36       amd64        Vi IMproved - enhanced vi editor

8.2 卸载 vim 主包

# apt remove -y vim
Reading package lists...
Building dependency tree...
Reading state information...
The following packages will be REMOVED:
  vim
0 upgraded, 0 newly installed, 1 to remove and 8 not upgraded.
After this operation, 186 kB disk space will be freed.
(Reading database ... 123456 files and directories currently installed.)
Removing vim (2:8.2.3995-1ubuntu2.36) ...
update-alternatives: using /usr/bin/nvim to provide /usr/bin/vim (vim) in auto mode

💡 说明:卸载 vim 时 update-alternatives 自动把 /usr/bin/vim 切回剩余候选 nvim(auto mode),这正是 alternatives 机制在卸载场景下的表现。

8.3 连依赖一起清理

apt remove 保留 vim-common、vim-runtime;彻底清理用 apt purge(同时删除配置文件)或 apt autoremove(清理不再需要的依赖):

# apt purge -y vim vim-common vim-runtime
# apt autoremove -y

⚠️ 注意:若服务器上需要保留 Neovim,卸载 vim 后 vim 命令会指向 nvim(见 8.2 回显),属正常现象;如需完全移除 vim 相关命令,按需处理。

九、参考链接