Vim 是 VIMproved 的缩写,即 Vim 编辑器是 Vi 编辑器的改进版。Vim 采用模式编辑的理念,其提供多种操作模式,按键在不同的模式下作用不同。 如,普通模式用于浏览文件,插入模式用于插入文本,可视模式下可以选择行或文本块,命令模式可以执行命令等等。

和模式编辑紧密相连的概念是 操作符动作操作符 指的是开始某个行为,如修改、删除或者选择文本;之后你要用一个 动作 来指定需要操作的文本区域。比如,要改变括号内的文本,需要执行 ci((读做 change inner parentheses);删除整个段落的内容,需要执行 dap(读做:delete around paragraph)。

Vim 存在一个兼容模式,该模式让 Vim 兼容 Vi。在兼容模式下运行时,Vim 大部分增强及改善的功能将不可用。需注意的是,这种模式下,Vim 并不能简单等同 Vi - 此模式只是设定一些类似 Vi 编辑器工作方式的默认选项。

Vim 包含三种不同的操作模式,即 命令模式(或者叫 普通模式)、插入模式底行命令模式。使用 ESCi/o/a等,: 来切换不同模式。打开 Vim 时,默认停留在 命令模式,按 i 等键可以进入 插入模式 进行文本编辑,按 ESC 键可以退出编辑模式,然后输入 : 可进入底行命令模式。编辑模式和底行命令模式都只能从命令模式进入,ESC 键主要用于退出编辑模式和底行命令模式。另外,还有两种常用的工作模式,可视模式 (Visual-mode)粘贴模式(Paste-mode)

在以下的描述中,所有以 : 开头的命令都表示在底行命令模式下的命令,所有不以 : 开头的命令都表示在普通模式下的命令。

Vim 自带一个交互式的教程,包含一些基础的操作,可以通过终端运行以下命令打开教程来练习使用:

$ vimtutor

基础

打开文件

从命令行中打开:

打开文件
$ vim filename

当指定一个目录时会打开一个目录树
$ vim dirname

打开多个文件并横向分屏
$ vim -o file1 file2 file3

打开多个文件并纵向分屏
$ vim -o file1 file2 file3

打开文件并调到指定行
$ vim filename +num

在打开文件前,先执行指定的命令
vim -c cmd filename

恢复上次异常退出的文件
vim -r filename

以只读的方式打开文件,但可以强制保存
vim -R filename

只读的方式打开文件,不可以强制保存
vim -M filename

打开文件,并将光标停留在第一个找到的 string 上
vim +/string filename

用已有的 vim 进程打开指定的文件
vim --remote filename

从 Vim 中打开文件:

打开文件
:e filename

打开目录树(默认打开当前目录)
:Ex dirname

打开一个文件并横向分屏
:sp filename
:split filename

打开一个文件并纵向分屏
:vsp filename
:vsplit filename

打开以光标所在字符串为文件名的文件
gf

保存与退出

:w -- 保存修改
:wa[ll] -- 保存所有修改过的窗口
:wq -- 保存并退出
:x -- 保存并退出
ZZ -- 保存并退出

:n1,n2w filename -- 选择性保存从某n1行到另n2行的内容

:saveas newfilename -- 另存为

:q[uit]  -- 退出 vim
:q! -- 强制退出(放弃修改)
:qa[ll] -- 关闭所有窗口,退出vim

:close -- 关闭当前窗口

:only -- 只保留当前窗口,关闭其它窗口(CTRL-W o)

缓冲区

缓冲区是一个文件的内容占用的那部分 Vim 内存。每一份文件都是在他们自己独有的缓冲区打开的,每次文本都是作为缓冲区的一部分显示的。列出缓冲区可以用如下三个命令:

:buffers
:files
:ls

示例:

:ls
  1 %a   "test1"                        line 1
  2      "test2"                        line 0
  3      "test3"                        line 0

其中,第一列是文件编号,第二列是缓冲文件的状态,第三列是文件的名称,第四列是上一次编辑的位置。 缓冲文件的状态有如下几种(仅供参考):

  • - 非活动的缓冲区
  • a 当前被激活缓冲区(光标所在的缓冲区)
  • h 隐藏的缓冲区
  • % 当前的缓冲区(指当前窗口可见的缓冲区,因为可以分割窗口,可能有多个)
  • # 交换缓冲区
  • = 只读缓冲区
  • + 已经更改的缓冲区

切换工作区用 :buffer/:b 命令,或者用 :sbuffer/sb

:[s]b 编号
:[s]buffer 文件名

:[s]bn[ext]
:[s]bp[revious]
:[s]bl[ast]
:[s]bf[irst]

添加或删除缓冲区

添加一个文件到缓存区
:bad[d] 文件名

新建一个未命名的缓冲区
:enew

从缓冲区删除一个文件
:bdel[ete] 文件名/编号

参数列表

缓冲区列表是 Vim 的特性。在这之前的 vi 中,仅仅只有参数列表,参数列表在 Vim 中依旧可以使用。

每一个通过 shell 命令传递给 Vim 的文件名都被记录在一个参数列表中。可以有多个参数列表:默认情况下所有参数都被放在全局参数列表下,但是你可以使用 :arglocal 命令去创建一个新的本地窗口的参数列表。

使用 :args 命令可以列出当前参数。使用 :next:previous:first:last 命令可以切换在参数列表中的文件。通过使用 :argadd:argdelete 或者 :args 等命令改变参数列表。

参数列表在有些情况下是非常有用的,如使用 :argdo 做批处理,示例:

:args **/*.[ch]
:argdo %s/foo/bar/ge | update

以上命令将替换掉当前目录以及其子目录中所有的 C 源文件和头文件中的 “foo” 为 “bar”。:argdo 命令会对参数列表中的文件执行相同的动作。

更多关于参数列表的信息,可以通过查阅离线文档了解 :h argument-list

标签/窗口

窗口 是缓冲区更上一层的视窗。如果想同时查看几个文件或者查看同一文件的不同位置,则可以开启多个窗口。窗口可以水平或者竖直分割并且现有窗口的高度和宽度可以被调节。

打开多个窗口可以用以下命令:

# 横向打开一个新窗口
:sp[lit]
:sp[lit] filename
:new
:new filename

# 纵向打开一个新窗口
:vsp[lit]
:vsp[lit] filename

ctrl-w s  上下分割当前文件
ctrl-w v  左右分割当前文件

# 水平分割一个窗口,浏览文件系统
:Sex

# 垂直分割一个窗口,浏览文件系统
:Vex

窗口大小纵向调整:

:ctrl+w +  纵向扩大(行数增加)
:ctrl+w -  纵向缩小 (行数减少)
:res(ize) num  如 :res 5,显示行数调整为5行
:res(ize) +num  把当前窗口高度增加 num 行
:res(ize) -num  把当前窗口高度减少 num 行

窗口大小横向调整:

:vertical res(ize) num  指定当前窗口为num列
:vertical res(ize)+num  把当前窗口增加num列
:vertical res(ize)-num  把当前窗口减少num列

标签页 是可以容纳一系列 窗口 的容器。打开标签可以使用以下命令

:tabe[dit] [文件名]
:tabnew [文件名]

切换标签页:

# 下一个标签
gt
:tabn[ext]

# 上一个标签页
gT
:tabp[revious]

# 快速切换到第一个标签或最后一个标签
:tabfir[st]  :tabr[ewind]
:tabl[ast]

使用 :ectrl+^ 可以回到刚才编辑的文件,这已操作感觉很实用。

其他操作:

# 显示所有标签
:tabs

# 关闭标签
:tabc[lose]

# 关闭所有其他标签页,只保留当前活动标签页
:tabo[nly]

# 将当前标签页移到第 n 个标签页之后
:tabm[ove] N

# 同时在多个标签页中执行命令
:tabdo command

如 :tabdo %s/food/drink/g 可一次完成对所有文件的替换操作,而不用针对每个文件重复操作

随便说一下,缓冲区列表是全局可见的,可以在任何标签中访问任何一个缓冲区。

寄存器

寄存器是存储文本内容的空间。常用的「复制」操作就是把文本存储到寄存器,「粘贴」操作就是把文本从寄存器中读出来。例如常用的复制快捷键 y,粘贴快捷键 p,也都是对寄存器(匿名寄存器 Unnamed)的操作。

Vim 提供了大致 10 类共 48 个寄存器。寄存器以 " 开头进行标记,即 "{register}。可以用 "{register}y 来拷贝到 {register} 中, 用 "{register}p 来粘贴 {register} 中的内容。如:"ayy 可以拷贝当前行到寄存器 a 中,而 "ap 则可以粘贴寄存器 a 中的内容。

寄存器大致分为以下几类:

  • 匿名寄存器 ""
  • 编号寄存器 "0"9
  • 小删除寄存器 "-
  • 26 个命名寄存器 "a"z
  • 3 个只读寄存器 ":, "., "%
  • Buffer 交替文件寄存器 "#
  • 表达式寄存器 "=
  • 选区和拖放寄存器 "*, "+, "~
  • 黑洞寄存器 "_
  • 搜索模式寄存器 "/

动作/操作符/文本对象

动作 值移动光标的操作,如熟悉 h、j、k 和 l,以及 w 和 b 等,/, ? 也是一个动作。他们都可以搭配数字使用,比如 2?the 可以将光标移动到倒数第二个 "the" 出现的位置。可以通过 :h navigation 来查看支持的动作。

操作符 是对某个区域文本执行的操作。如,d、~、gU 和 > 都是操作符。这些操作符既可以在普通模式下使用,也可以在可视模式下使用。在普通模式中,顺序是先按操作符,再按动作指令,比如 >j。在可视模式中,选中区域后直接按操作符即可,比如 Vjd。操作符也可以搭配数字使用,比如 2gUw 可以将当前单词以及下一个单词转成大写。由于动作和操作符都可以搭配数字使用,因此 2gU2w 与执行两次 gU2w 效果是相同的。有关操作符的详细帮助,可以通过 :h operator 来查看。

文本对象 是双向选择的文本(动作是单向选择的)。文本对象操作可以选中符号(比如括号、中括号和大括号等)标记的范围内文本,也作用于整个单词、整个句子等其他情况。文本对象不能用于普通模式中移动光标的操作,因为光标还没有智能到可以向两个方向同时跳转。但这个功能可以在可视模式中实现,因为在对象的一端选中的情况下,光标只需要跳转到另一端就可以了。可以通过 :h text-objects 来详细了解关于文本对象的内容

文本对象操作一般用 ia 加上对象标识符操作,其中 i 表示在对象内(英文 inner)操作,a 表示对整个对象(英文 around)操作,这时开头和结尾的空格都会被考虑进来。如,diw 可以删除当前单词,ca( 可以改变括号以及括号中的内容。

文本对象同样可以与数字搭配使用。比如,像 ((( ))) 这样的文本,假如光标位于最内层的括号上或最内层的括号内,那么 d2a( 将会删除从最内层开始的两对括号,以及他们之间的所有内容。其实,d2a( 这个操作等同于 2da(。需要注意的是,在 Vim 的命令中,如果有两处都可以接收数字作为参数,那么最终结果就等同于两个数字相乘。在这里,d 与 a( 都是可以接收参数的,一个参数是 1,另一个是 2,也可以把它们相乘然后放到最前面。

配置

Vim 是一个可高度定制的编辑器,对编辑器的定制通过添加配置项和插件来完成,其读取配置文件的顺序可以通过:

:version

来查看,如以下为 MacOSX 上的结果:

  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"

一些配置示例:https://github.com/kuanghy/vimconfig.

常用配置列举:

" 不要工作在 Vi 兼容的模式下
:set nocp

" 显示不可视字符
:set list

" 开启/取消粘贴模式
:set paste
:set nopaste

" 显示/隐藏行号
:set number
:set nu
:set nonumber
:set nonu

" 在搜索时忽略/不忽略大小写
:set ic
:set noic

:set fileencoding        " 查看文件编码
:set fileencoding=utf-8  " 转换文件编码

:set fileformat?         " 查看文件格式
:set fileformat=unix     " 设置文件格式

:set bomb?     " 查询 BOM
:set bomb      " 添加 BOM
:set nobomb    " 删除BOM

配置 Vim 支持系统剪切板,首先查看 Vim 本身是否支持:

vim --version | grep clipboard

输出的 clipboard 和 xterm_clipboard 前面为加号(+)表示支持,为减号(-)表示不支持。或者:

:echo has('clipboard')

输出 1 表示支持,输出 0 表示不支持。如果不支持可以尝试重新编译安装。支持系统剪切板后便可使用如下命令操作剪切板:

"+y 复制内容到系统剪切板
"+p 把系统剪切板里的内容粘贴到 vim

也可以配置 Vim 与系统剪切板共享复制粘贴(设置剪切板为匿名寄存器):

if has("clipboard")
    set clipboard=unnamed  " copy to the system clipboard
    if has("unnamedplus")  " X11 support
        set clipboard+=unnamedplus
    endif
endif

移动

单位级

  • h 向左一字符
  • j 下一行
  • k 上一行
  • l 向右一字符

单词级

  • w or W 向右移动到下一单词开头
  • e or E 向右移动到单词结尾
  • b or B 向左移动到单词开头

注:所有小写单词都是以分词符作为单词界限,大写字母以空格作为界限。

块级

  • ( 移动到当前句子开头
  • ) 移动到下一句子开头
  • { 移动到当前段落开头
  • } 移动到下一段落开头
  • + 移动到下一行开头
  • - 移动到上一行开头
  • gg 到文档第一行
  • G 到文档最后一行
  • 0 到行首(第 1 列)
  • ^ 到第一个非空白字符
  • $ 到行尾
  • g_ 到当前行的最后一个非空格字符处
  • H 到屏幕的首行
  • L 到屏幕尾行
  • M 到屏幕中间
  • % 到匹配的括号(包括小括号、中括号、大括号)
  • zt 将当前行移到屏幕顶部
  • zz 将当前行移到屏幕中部
  • zb 将当前行移到屏幕底部
  • Ctrl-d 向下移动半页
  • Ctrl-u 向上移动半页
  • Ctrl-f 向下移动一页
  • Ctrl-b 向上移动一页
  • :<N> or <N>gg 跳转到第 N 行
  • :+<N> or <N>j 向下跳 N 行
  • :-<N> or <N>k 向上跳 N 行

编辑

插入

执行插入操作需进入 插入模式,以下操作均可实现插入操作:

i 在当前光标位置插入字符
I 在当前行首位置插入字符
o 在当前行往下插入新的一空行
O 在当前行往上插入新的一空行
a 在当前光标后追加字符
A 在当前行尾追加字符
r 替换当前字符
R 替换当前光标的字符直到退出插入模式(按ESC)
c 删除选中文本并进入插入模式
C 删除至行尾并进入插入模式
s 删除当前字符并进入插入模式
S 删除当前行并进入插入模式
:r filename 把文件的内容插入当前行的下一行
:r! command 把命令返回的结果插入到当前行的下一行

复制与粘贴

y (yanking)操作符用于复制文本,p 操作符用于粘贴文本,以下列举常用的复制操作:

yw 拷贝当前的一个单词
y0 拷贝的范围是当前光标处到行首
y^ 同上
y$ 拷贝的范围是当前光标处到行尾
yy 拷贝当前行
Y  同上
Nyy 从当前行开始拷贝 N 行
yf + 字符 复制从当前字符到指定字符
yG 拷贝当前行到文件尾
ygg 拷贝当前行到文件头
ggyG 复制整个文件
"+y 复制到系统剪切板
"+p 粘贴剪切板中的内容

Vim 还提供了基于底行命令模式的整行复制与移动命令:

:[range]co[py]{address}
:[range]t{address}

:[range]m[ove]{address}

命令 copy 可以简写成 :co 或者 :t,命令 move 可以简写成 m。示例:

:t5    拷贝当前行到第5行的下一行
:t.    拷贝当前行到光标下一行,相当于 `Yp` 或者 `yyp`
:t$    拷贝当前行到文件最后一行

:50,100move200    移动50行到100行的内容到第200行后面

删除/修改

x 操作符用于删除单个字符或选中的文本,d 操作符用于删除单词、行、选中的文本等:

x   删除当前光标处字符
X   删除光标所在前一个字符
dw  删除一个单词
de  删除到本单词末尾
dE  删除到本单词末尾包括标点在内
db  删除到前一个单词
dB  删除到前一个单词包括标点在内
d0  删除光标位置到本行开头
d$  删除从当前光标到行尾
D   同上
dd  删除一行
df + 字符  删除从当前字符到指定字符

# 删除括号或者引号中的内容
di(
di'
di"

# 包括括号或者引号一起删除
da(
da'
da"

c 操作符用于修改文本内容,即执行 c 操作时,会先删除选中的文本,然后再自动切换到插入模式:

cw         修改一个单词
c$         修改从当前字符到行尾
cc         修改整行
C          修改当前光标至行尾
cf + 字符   修改从当前字符到指定字符

# 修改括号或者引号中的内容
ci(
ci'
ci"

# 包括括号或者引号一起修改
ca(
ca'
ca"

撤销/重做/合并行

  • u 撤销
  • ctrl-r 重做

选中行,在执行 J 操作符可合并多行到一行,默认合并当前行与下一行。

范围

在使用 Vim 的一些操作时,无非就两步,即先选择要操作的内容,在执行相应的操作。要操作的内容即是这里所说的 范围(Ranges)。可以使用 :h cmdline-ranges 来查看有关命令操作范围的有关信息。

  • 大多数命令都可以加一个数字,用于指明操作范围
  • 范围可以是一个行号,用于指定某一行
  • 范围也可以是一对通过 ,; 分割的行号
  • 大部分命令,默认只作用于当前行
  • :g[lobal] 默认会作用于所有行

范围的使用是十分直观的,以下是一些删除的例子(其中,:d:delete 的缩写):

命令 操作范围
:d 当前行
:.d 当前行
:1d 第一行
:$d 最后一行
:1,$d 所有行
:%d 所有行(这是 1,$ 的语法糖)
:.,5d 当前行至第 5 行
:,5d 当前行至第 5 行
:,+3d 当前行及接下来的 3 行
:1,+3d 第一行至当前行再加 3 行
:,-3d 当前行及向上的 3 行(Vim 会弹出提示信息,因为这是一个保留的范围)
:3,'xdelete 第三行至标注 为 x 的那一行
:/^foo/,$delete 当前行以下,以字符 "foo" 开头的那一行至结尾
:/^foo/+1,$delete 当前行以下,以字符 "foo" 开头的那一行的下一行至结尾

其中, . 表示当前行,$表示结束,% 表示所有行,-+ 分别表示前后的偏移量。

; 也可以用于表示范围。区别在于,a,b 的 b 是以当前行作为参考的。而 a;b 的 b 是以 a 行作为参考的。比如说,现在的光标在第 5 行。这时 :1,+1d 会删除第 1 行至第 6 行,而 :1;+1d 会删除第 1 行和第 2 行。

搜索

文档内查找

  • * 向后查找光标当前所在单词
  • # 向前查找光标当前所在单词
  • g* 向后查找光标当前所在单词的字符序列,如当前单词为 foo 时,foobar 也会被找到
  • g# 向前查找光标当前所在单词的字符序列
  • /<pattern> 向后查找指定字符串
  • ?<pattern> 向前查找指定字符串

在执行查找命令后,按 n 会继续查找下一个,N 会继续查找上一个。注意,n 和 N 是有方向性的,若之前通过 * 查找,则 n 会继续向文档尾方向查找,N 向文档首方向;反之,若你通过 # 查找,则 n 指向文档首,N 指向文档尾。

在使用 /? 查找时,加入 \c 表示大小写不敏感查找,\C 表示大小写敏感查找。

行内查找

  • f<X> 当前行内向行尾方向查找并定位到字符 X
  • t<X> 当前行内向行尾方向查找并定位到字符 X 之前
  • F<X> 当前行内向行首方向查找并定位到字符 X
  • T<X> 当前行内向行首方向查找并定位到字符 X 之后

在执行查找命令后,; 继续向当前方向查找下一个字符,, 向当前相反方向查找下一个字符。

括号匹配查找

可以使用 % 对 ( 和 ),[ 和 ],{ 和 } 进行匹配查找,当光标位于其中一个 符号 上时,按下 %,光标会跳到与之匹配的另外一个符号上。

正则

元字符

. 匹配任意字符
[abc] 匹配方括号中的任意一个字符,可用-表示字符范围。如[a-z0-9]匹配小写字母和数字
[^abc] 匹配除方括号中字符之外的任意字符
\d 匹配阿拉伯数字,等同于[0-9]
\D 匹配阿拉伯数字之外的任意字符,等同于[^0-9]
\x 匹配十六进制数字,等同于[0-9A-Fa-f]
\X 匹配十六进制数字之外的任意字符,等同于[^0-9A-Fa-f]
\l 匹配[a-z]
\L 匹配[^a-z]
\u 匹配[A-Z]
\U 匹配[^A-Z]
\w 匹配单词字母,等同于[0-9A-Za-z_]
\W 匹配单词字母之外的任意字符,等同于[^0-9A-Za-z_]
\t 匹配<TAB>字符
\s 匹配空白字符,等同于[\t]
\S 匹配非空白字符,等同于[^\t]

表示数量的元字符

注意:下面除了 * 以外,其他都需要在前面加上 \

*    匹配 0-任意个
\+   匹配 1-任意个
\?   匹配 0-1 个
\{n,m}   匹配 n-m 个
\{n}     匹配 n 个
\{n,}    匹配 n-任意个
\{,m}    匹配 0-m 个

表示位置的元字符

$  匹配行尾
^  匹配行首
\<  匹配单词词首
\>  匹配单词词尾

需转义的字符

\*  匹配* 字符
\.  匹配. 字符
\/  匹配 / 字符
\\   匹配 \ 字符
\[  匹配 [ 字符
\]  匹配 ] 字符

在正则表达式中以 \(\) 括起来的内容表示一个分组,在正则表达式后面使用的时候可以用 \1\2 等变量(1、2 表示分组序号)来访问 \(\) 中的内容。如 \(foo\).*\1 可以配置 foo_bar_foo 等字符串。

需要同时查找多个字符串,可以使用 "或" 运算符,即 \|。如 /foo\|bar 会同时查找 foo 和 bar 两个字符;再如 /end\(if\|while\|for\) 匹配 "endif","endwhile" 和 "endfor" 字符串。

在 Vim 中默认使用的搜索模式被称为 magic 模式。在这种模式下,Vim 自动为某些额外的符号赋予特殊含义,例如 .*[ 等。这种模式的初衷是想能在 Vim 中更容易地构造简单的正则表达式,但它却没能为诸如 +?(){ 等符号赋予特殊含义,这些符号还必须经过转义才具有特殊含义。

Vim 还提供了 very magic 搜索模式 用于查找字符串,在这种模式下统一了所有特殊符号的规则:除下划线 _、大小写字母以及数字 0 到 9 之外的所有字符都具有特殊含义。该模式使用 \v 开启,如:/\vend(if|while|for),这样,用于为特殊字符进行转义的反斜杠字符便可以去掉,使得匹配表达式的可读性更强。如该种模式下,便可以用 <> 符号表示单词定界符,而无需转义。

此外,还有 very nomagic 搜索模式,在该模式中有且只有反斜杠 \ 具有特殊意义,即消除了附加在 .* 以及 ? 等大多数字符上的特殊含义。也就是绝大多数字符都按原义进行查找匹配,该模式使用 \V 开启。如要查找 a.k.a. 字符串时,如果不使用 very nomagic 模式搜索,则其他的字符串也可能被找到,如 backward,除非使用转义 /a\.k\.a\.。而使用 very nomagic 模式就要简单得多 /\Va.k.a.。 `

替换

Vim 中的简单替换可以使用 r/Rc/Cs/S 等操作符:

r  替换当前字符
R  替换当前光标的字符直到退出替换模式(按ESC)
cw  替换当前单词
cc  替换当前行,相当于 S
C   替换当前位置到行尾,相当于 c$

此外,还可以使用 :s (substitute)操作,配置范围、模式查找等实现更加强大的替换功能,其格式为:

[range]s/{pattern}/{string}/[flags]

flags 有如下四个选项

  • c confirm,每次替换前询问
  • e error,不显示错误
  • g globle,不询问,整行替换。如果不加 g 选项,则只替换每行的第一个匹配到的字符串
  • i ignore,忽略大小写

可以同时使用多个 flag,如 cgi 表示不区分大小写,整行替换,替换前询问。示例:

:1,10s/from/to/g  表示在第1到第10行(包含第1,第10行)之间搜索替换
:10s/from/to/g  表示只在第10行搜索替换
:%s/from/to/g  表示在所有行中搜索替换
:1,$s/from/to/g  同上

:%s/\s+$//g  删除行尾空格
:%s/^\s*// 或 :%s/^ *//  删除行首多余空格
:%s/^$// 或 :g/^$/d  删除沒有內容的空行
:%s/^\s*$// 或 :g/^\s*$/d  删除包含有空格组成的空行
:%s/^[ |\t]*$// 或 :g/^[ |\t]*$/d  删除以空格或 tab 开头到结尾的空行

标记

标记 可以理解为 书签,在某处打上标记后,可以快速从别处跳转回来。标记可以分为三类:

  • a-z 用户设置,仅对当前的一个文件生效,也就意味着只可以在当前文件中跳转
  • A-Z 用户设置,全局标记,可以作用于不同文件。大写标注也称为「文件标注」。跳转时有可能会切换到另一个缓冲区
  • 0-9 由 viminfo 记录,0 代表 viminfo 最后一次被写入的位置。实际使用中,就代表 Vim 进程最后一次结束的位置。1 代表 Vim 进程倒数第二次结束的位置,以此类推

定义一个标记用 m 操作符,如 ma 标记当前问题为 a 标记。在标记间跳转用 ' / g' 或者 ` / g` 加上标注名。单引号与倒引号的区别在与,单引号会跳转回被标记行的第一个非空字符,而倒引号会跳转回被标记行的被标记列。

还有一些其他的跳转方式:

'[ 与 `[    上一次修改或复制的第一行或第一个字符
'] 与 `]    上一次修改或复制的最后一行或最后一个字符
'< 与 `<    上一次在可视模式下选取的第一行或第一个字符
'> 与 `>    上一次在可视模式下选取的最后一行或最后一个字符
'' 与 `'    上一次跳转之前的光标位置
'" 与 `"    上一次关闭当前缓冲区时的光标位置
'^ 与 `^    上一次插入字符后的光标位置
'. 与 `.    上一次修改文本后的光标位置
'( 与 `(    当前句子的开头
') 与 `)    当前句子的结尾
'{ 与 `{    当前段落的开头
'} 与 `}    当前段落的结尾

标记也可以搭配 范围 一起使用。如果在可视模式下选取一些文本,然后按下 :,这时命令行会被填充为 :'<,'>,这即表示在可视模式下选取的范围。

使用 :marks 可以查看当前所有的标记,使用 :h mark-motions 更多关于标记的帮助信息。

寄存器

寄存器主要用于存储文本内容,其为复制粘贴功能提供辅助。寄存器的访问和操作使用冒号 " 作为前缀,如 "ayy 表示复制当前行到 a 寄存器,"ap 表示粘贴寄存器中的内容。使用 :reg 可以查看当前所有寄存器中的内容,使用 :reg "{register_name} 可以查看指定寄存器的内容。

匿名寄存器(Unnamed): 保存最近一次的复制或删除操作 (d, c, s, x, y)。如果当前使用的 vim 版本支持剪切板,则可以将匿名寄存器定向到剪切板上,这样便可以与系统共享剪切板:

set clipboard=unnamed

编号寄存器(Numbered): 即以数字命令的寄存器,从 "0"9 共 10 个。其中 "0 保存着最近一次复制的内容,"1"9 保存着删除掉的字符串。 删除操作符包括 s, c, d, x。 删除掉的字符串会被存到 "1 中,上次删除的则会被存到 "2 中,以此类推。

小删除寄存器(Small delete): 保存最近一次行内删除的内容,如 dw 删除一个词,d9l 删除 9 个字符,cb 向前更改一个词等。

命名寄存器(Named): 其有 "a"z 共 26 个,这类寄存器只有在被指定时才会被定义和使用。其实在录制宏时,所有键盘操作会以字符串的形式存到寄存器中。需注意的是,寄存器名也可以为大写,当使用小写字母进行操作时会覆盖当前寄存器内容,而当使用大写字母进行操作时会追加当前寄存器内容。如 "Ayy 会复制当前行并追加到寄存器 a 中。

只读寄存器(Read-only): 共有 3 个只读寄存器,其值是由 Vim 提供,不允许改变:

  • ".:最近一次 insert 模式中插入的文本。注意 . 命令用于重复上次操作,而 ". 用于存储上次插入
  • "%:当前文件名,不是全路径,也不是纯文件名,而是从当前 Vim 的工作目录到该文件的路径,即相对路径
  • "::最近一次底行命令模式下使用的命令。正如 @a 可以执行 "a 寄存器中的宏一样,@: 可以执行上次命令

交替文件寄存器(Alternate buffer): 交替文件寄存器 "# 存储着当前 Vim 窗口(Window)的交替文件。交替文件是指 Buffer 中的上一个文件,可通过 Ctrl+^ 来切换交替文件与当前文件。

表达式寄存器(Expression): 表达式寄存器 "= 主要用于计算 Vim 脚本的返回值,并插入到文本中。当键入 "= 后光标会移动到命令行,此时可以输入任何 Vim 脚本的表达式,例如 3+2,按下回车并且 p 则会得到 5。这对于调试 Vim 脚本是非常有用的,比如调用一个函数看它是否有正确的返回值。

选择(Selection)和拖放(Drop)寄存器: 包括 "*, "+, 和 "~,这三个寄存器的行为是和 GUI 相关的。

"*"+ 在 Mac 和 Windows 中,都是指系统剪切板(clipboard),例如 "*yy 即可复制当前行到剪切板。 以供其他程序中粘贴。其他程序中复制的内容也会被存储到这两个寄存器中。 在X11系统中(绝大多数带有桌面环境的Linux发行版),二者是有区别的:

  • "* 指 X11 中的 PRIMARY 选区,即鼠标选中区域。在桌面系统中可按鼠标中键粘贴。
  • "+ 指 X11 中的 CLIPBOARD 选区,即系统剪切板。在桌面系统中可按 Ctrl+V 粘贴。

拖放寄存器 "~ 是只读的,当有文本拖拽到 Vim 时,被拖拽的文本被存储在 "~ 中。Vim 默认的行为是将 "~ 中内容插入到光标所在位置。

黑洞寄存器(Black hole): 所有删除或拷贝到黑洞寄存器 "_ 的文本将会消失,这是为了在删除文本的同时不影响任何寄存器的值,其通常用于 Vim 脚本中。

搜索寄存器(Last search pattern): 搜索寄存器 "/ 用于存储上一次搜索的关键词,即最近一次通过 /?:global 等命令调用的匹配条件。该寄存器是可写的,例如: let @/ = "harttle" 将会把 "harttle" 写入该寄存器。下次搜索时不输入搜索词直接回车便会搜索 "harttle"。

在 Vim 中可以录制一系列按键动作,并把他们存储到寄存器中,这便是 。宏是一系列操作的集合,它可以将一些复杂的操作进行记录,之后可以进行回放。

操作符 q 用于开启或结束宏录制,按 q{register} 开始进入录制,然后再按 q 可结束录制。如果要对宏进行回放,按 @{register}

示例一,在行末加分号,命令为:

qa
$a;<Esc>j$,
q

命令的含义是:开启宏录制,并记录到寄存器 a 中,先移动到行尾,然后在行尾插入分号,接着退到普通模式,再把光标移动到下一行的末尾,最后结束宏录制。录制结束后,输入 @a 便会执行宏中的操作,5@m 表示重复执行 5 次宏操作。

示例二,从 1 到 100,每行 +1,首先在第一行插入 1,然后光标定位到第一行的 “1” 上,执行宏录制:

qb
yyp<Ctrl>a
q

命令的含义是:开始录制宏到寄存器 b 中,yy 复制当前行,p 粘贴到下一下, <Ctrl>a 让当前光标所在数字加 1。然后执行 98@b 将宏回访 98 遍即可实现将数字加到 100。

其他

插入计算结果

在插入模式下,输入 ctrl-r =,然后输入表达式,就能在光标处得到计算结果。

保存无权限的只读文件

如果打开了一些没有权限编辑的只读文件,并修改了内容,且希望对修改进行保存时,可以用如下命令(前提是当前用户有 sudo 权限):

:w !sudo tee %

编辑远程文件

Vim 内置的插件 netrw 支持对远程文件的编辑。它的原理是将远程的文件通过 scp 复制到本地的临时文件中,再用这个文件打开一个缓冲区,然后在保存时再将文件复制回远程位置。以下命令列举在本地打开 SSH 远程服务器上文件的方式:

:e scp://user@remotehost/.vimrc

:e scp://user@remotehost/~/.vimrc

:e scp://user@remotehost//etc/passwd

也可以配置 ~/.ssh/config 文件,使得命令更加简单,如 ~/.ssh/config 存在如下配置:

Host test
    HostName test.site.com
    Port 23456
    User test

那么编辑文件的命令就可以变得很简单:

:e scp://test/.vimrc

查看安装的所有脚本

:scriptnames

有用的快捷键

K    打开光标所在词的 manpage
.    重复上一次的操作,如 dd 删除一行后,. 命令可继续执行删除

参考