00-前言

Vi 和 Vim

Vi原本是一个字处理软件,功能太过于强大,常常用于程序开发工具,Vim是增强的Vi。

为什么学 Vim?

不是为了打造炫酷IDE而装B,而是经常需要在服务器上做文本编辑或者日志查看、搜索等工作,而服务器是不提供桌面环境的,只能在shell命令行下操作,nano的功能过于简单,emacs上手门槛太高,所以懂一点Vi可以提高工作效率,vi 可以很复杂很复杂,几本书也讲不完,我们只需要懂一些基础操作,就能给工作带来很大的便利。

Vim中有很多的组合思想,和Unix的设计哲学很匹配,每个小命令做好一件事,合起来则能干很大的事,比如命令dd是删除,p是粘贴,如果你连续敲ddp就会发现两行相互交换了,交换2行这个操作在写代码或者重构代码的时候会经常用到,这种设计理念值得品味。

Vim 技能参考图

01-vim 的发展历史

vi 的创始人叫比尔乔伊,是 sun 公司的合伙人,vi 的前身可以说是 ed,关于 vi 的发展历史,我总结了这张节点图。

文字版本如下:

1964 年,在加州伯克利分校,由于早期 Unix 分时操作系统的编写过程中,需要编写内核,缺少命令行文本编辑工具,当时的大神发明了 QED 小程序,意思就是 quick editor 快速编辑器。当时在贝尔实验室,肯汤普森其中一项工作就是把 QED 移植到大型的分时操作系统之上,后面肯汤普森发明了 Unix,QED 也被带进了 Unix,成为了 ed 组件,此外这个 ed 还衍生出了一系列的 Unix 命令,比如著名的 grep 和 sed,事实上 ed 已经成为当时 Unix 操作系统的标配。

1975 年肯汤普森回到伯克利分校,准备把 Unix 移植到伯克利分校自己的电脑上(也就是后来的伯克利软件套装 BSD),当时 Unix 很流行,所以用 ed 的人也很多,但是大多数人来说这玩意儿真的是太难用了。

有一个大学的讲师开始着手修改 ed,并且改名为 em(the editor for mortals 给凡人用的编辑器),在学校读研的学生比尔乔伊受到很大的启发,要来了 em 的源代码,在其基础上不断的修改,改名为 en,一直修改,一直改名,最后名称变为了 ex,1977 年他在 ex 的基础上,增加全屏幕显示的功能,并命名为 ex 的可视化模式,别名 vi。下图就是他。

当时他使用的电脑的布局是这样的:

主要看这个键盘的方向键(当时其他的键盘方向大都也是横起的),不是我们今天打游戏的WASD,这个键盘的 ESC 和 HOME 键也比较特殊,难怪 Unix 的 ~ 表示主目录。此后 vi 一直改善,因为当时其实还没有鼠标,所以 vi 一直都是一个基于模式(需要在编辑模式和命令模式之间来回切换)的软件,拥有各式各样的命令。

1982 年,三个斯坦福的学生创办了 sun 公司,比尔乔伊也被挖了过去,但是伯克利分校的 BSD 的 vi 因为 AT&T 挑起的官司,控告 BSD 侵犯了知识产权, 因为 vi 是基于 BSD 开发的,也被禁止在伯克利大学继续开发迭代,但是很多 vi 的粉丝并不愿意使用 emacs (理查德斯托曼搞的自由免费开源的 GNU 软件),在 1990 年有人就发布了 vi 的克隆版 nvi (new vi),被纳入了 BSD,一直沿用至今。

2000 年,有人基于比尔乔伊的源代码,冒死把 vi 发布到托瓦尔兹林纳斯的 linux 和 FreeBSD 操作上,但随着相关授权的解除,vi 最终成了合法的版本。由于 linux 的普及,以及 Mac OSX 和 BSD 变种流行, vi 当仁不让成为各大系统的标配,成为了编辑器的王者。

从 vi 发布以后,很多人模仿克隆,Vim 脱颖而出,它把工作重点方放在了兼容性处理上,1993 年正式使用 Vim (vi improvede) 为名称发布,距今已经是 30 年了,在最流行的开发环境中,2019 年的 StackOverflow 的调查中 Vim 排名第 4 位,大约 25% 的 Web 开发人员在使用它。

今天,Vim 和 Emacs 这 2 大阵营的人经常在掐架(就像是争论到底学 Java 好还是 PHP 好),其实理解是高手在切磋武艺更合适,它们都是了不起的软件,并且见证了整个计算机和操作系统的发展历史。

vi 为纯键盘操作而优化,所以熟悉 vi 的人很喜欢那种键盘不离手的感觉(不需要去操作鼠标),使用 vi 有一定的学习成本,但是熟悉后就可以起飞。他们爱它,恨它,最终离不开它。

今天有很多优秀的图形化文本编辑软件,我们并不一定非要用 vi 来做主流的 IDE(对,Vim 可以配置成非常牛掰的 IDE),但是熟悉 vi 会使你在服务器文件上更加自由的穿梭,游刃有余。

02-vim 的 3 种模式

在前面的 vi 发展历史里,我们知道 vi 转为键盘优化而生,是一个有模式的编辑软件。模式是使用 vi 需要客服的第一个障碍,或者说你把玩的第一个点。在终端中简单的输入 vi 或者 vim,就进入了软件的界面(先暂且不管 vi 和 vim 的区别),输入个 123456 看看,艹,界面上啥也没有呢? 这也能叫编辑软件?

这是因为 vi 有 2 种模式,一种是编辑模式,一种是命令模式。顾名思义编辑模式就是文本输入、修改等,而命令模式下你的按键都是一种对文本内容或者软件环境的一种控制,就像你用鼠标点了某个菜单一样。

怎么进入编辑模式呢? 按下键盘上的 a 进入编辑模式,这时你可以像正常输入文本那样进行输入了,vi 底部显示 --插入-- 字样,我们输入 hello vi. 这个文本。

输入完成以后,按下键盘左上角的 esc 键,你会发现底部的 --插入-- 字样消失了,这时候 vi 便返回到了命令模式,当前光标显示在 . 下面,我们不想要这个 . ,按下键盘上的 x,你猜对了,按了 x 后 . 被删除了,而且 vi 底部也没有显示 --插入-- 的字样,说明 vi 执行了删除字符的 x 命令以后,依旧处于命令模式下,我们再按 a 键进入编辑模式,在后面输入 m。

我们已经完成了内容的编辑,再次按 esc 键回到命令模式,继续输入 :wq hello.txt,你会发现这行命令显示在 vi 底部,这是命令模式中的底线命令模式,以英文冒号开头。

回车以后 vi 被关闭了,并且当前目录下生成了 hello.txt 文件,因为 wq 的意思是写入当前文件并且退出。

wangbo@wangbo-VirtualBox:~/test/vim$ ls -l total 4 -rw-rw-r-- 1 wangbo wangbo 10 12月 16 15:37 hello.txt wangbo@wangbo-VirtualBox:~/test/vim$ cat hello.txt hello vim wangbo@wangbo-VirtualBox:~/test/vim$ 恭喜,你完成了你的第一个 vi 文件,并且初步了解了 vi 的命令模式,从打开 vi 到退出,你应该还没有摸过鼠标,对吗?

小结一下,目前介绍了的 vi 的 2 种模式: 编辑模式、命令模式,命令模式又分为1)普通命令模式 和 2)底线命令模式,1) 对文本进行操作,2) 在文本命令模式下按 : 后显示在最下面(还有其他的激活命令比如和搜索有关的 ? /),对软件环境进行操作,不管当前是编辑模式还是底线命令模式,按 esc 键后一定回到普通命令模式。

此外,vi 还有一种模式叫: 可视模式,按命令 v 或者 V 进入,后面再详细学习。

利用底线命令模式,我们输入 :help wq 看看 wq 命令的帮助,接着连续两次输入 :q 退出 vi (或输入一次 :qa 命令),准备好进一步学习 vi 了吗?

03-变着花打开文件

上一节我们使用 vi 保存了一个文件 hello.txt,如果想再次对它进行编辑,怎么做呢? 直接输入 vim hello.txt 即可。我们按 o 键进入编辑模式,在第 2 行添加上文本 vim is graat 后,按 esc 回到命令模式,这次不用 :wq hello.txt 了,直接 : wq 保存退出。

wangbo@wangbo-VirtualBox:~/test/vim$ cat hello.txt
hello vim
vim is graat

我的天,great 写成了 graat,怎么办? 再次用 vim hello.txt 打开,不,我们这次直接打开定位到错误的那一行吧,输入 vim hello.txt +2,+2 表示打开文件定位到第 2 行,注意 +2 之前有一个空格。

由于我们还没学习如何在 vi 里移动跳转和做替换,先使用一个笨方法来修正这个错误,我们先按大写的 A 键,恩,你发现鼠标飞到了第 2 行的末尾并且 vi 进入了编辑模式。

使用键盘的退格键删除掉后面的 aat,然后输入 eat 后,esc + :wq 搞定。

如果已经进入了 vim,想打开文件怎么办? 输入 :e hello.txt 即可,也可以 :r hello.txt,后者是以只读的方式打开,编辑后需要另外存一个文件,如果需要多次中途需要保存,你可以随时输入 :w 来保存文件内容,执行 :q 后退出,如果不想保存修改,可以使用 :q! 退出,! 的意思就是强调,我确定我不需要保存修改的内容,实际上你对内容有修改后,:q 已经无法退出 vi 了。

vim 后如果跟多个文件,可以同时打开,输入 vim 1.txt 2.txt 后,打开以后底部显示 1.txt,按 a 写入 this is 1.txt,按 esc 回到命令模式输入 :w 保存。

怎么去编辑 2.txt 呢?继续输入 :bn,你发现底部显示 2.txt,说明我们当前编辑的是 1.txt 文件了。

试试 :bp、:b1、:b2、ctrl+6,发现可以在 1.txt 文件和 2.txt 之间切换了,等等。这里有一个严重的问题,必须要讲清楚了。难道 vi 就是这些死命令吗? 太无聊了吧,谁去记这么多命令啊?! 如果你想到了这个问题,说明你是一个思维敏捷的人,记得 linux 教程里 unix 的哲学吗? 仔细体会一下 wq 命令,w 和 q 其实是 2 个意思,一个代表写入 write,一个代表退出 quit,那么 bn、bp是什么鬼? bn = buffer next、bp = buffer prev,后一个缓冲区和前一个缓冲区,编辑的文件称为 vi 的缓冲区,b1 就是第 1 个缓冲区,b2 是第 2 个缓冲区,看吧? 你高兴的话,输入 :1b 和 :2b 也是行得通的,这些并不是死命令,vi 里面操作很多都是单词或命令的组合,所以千万不要去死记硬背,以后更常见的是命令的组合,理解了很多小命令,你甚至都可以自己推导出一些命令了。

对了,命令模式下输入 :ls 可以列出所有的缓冲区,和 shell 列文件差不多一个意思。

04-保存文件

关于文件的保存前面已经说了 :wq、q、:q!,但还有一些保存文件的细节需要知道。

当 vim 打开文件,如果没有任何修改,:q 可以顺利退出,如果有修改,vim 会提示 “已修改单位保存,可用 ! 强制执行”,执行 :q! 放弃所有修改,并且这种情况下 vim 不会再警告提示。

如果 vim 打开文件后,没有做任何修改,然后输入 :w 后接着 :q 退出(2次命令),文件有没有修改呢? 答案是有,文件的属性被修改了,:w 命令就是写入文件,文件的时间会修改。

wangbo@wangbo-VirtualBox:~/test/vim$ ls -l 1.txt
-rw-rw-r-- 1 wangbo wangbo 14 12月 17 10:50 1.txt
wangbo@wangbo-VirtualBox:~/test/vim$ vim 1.txt // 不修改内容,输入 :w 和 :q 后
wangbo@wangbo-VirtualBox:~/test/vim$ ls -l 1.txt
-rw-rw-r-- 1 wangbo wangbo 14 12月 17 10:51 1.txt

同理即便内容不改变 wq 也会写入文件,为了清楚的演示,给 ls 加上 --full-time 参数,它可以显示文件的准确时间。

wangbo@wangbo-VirtualBox:~/test/vim$ ls --full-time 1.txt
-rw-rw-r-- 1 wangbo wangbo 14 2020-12-17 10:58:04.489510495 +0800 1.txt
wangbo@wangbo-VirtualBox:~/test/vim$ vim 1.txt // 使用 :wq 退出
wangbo@wangbo-VirtualBox:~/test/vim$ ls --full-time 1.txt
-rw-rw-r-- 1 wangbo wangbo 14 2020-12-17 10:59:32.386770991 +0800 1.txt
wangbo@wangbo-VirtualBox:~/test/vim$ vim 1.txt // 使用 :wq 退出
wangbo@wangbo-VirtualBox:~/test/vim$ ls --full-time 1.txt
-rw-rw-r-- 1 wangbo wangbo 14 2020-12-17 10:59:42.105943076 +0800 1.txt

干嘛纠结这个? 改变文件时间有那么重要么? 有! 有些场合真的很重要,举 2 个例子:

第一: 在文件同步操作中,可能会先比较文件的属性,以此来判断文件是否被修改而不依赖于文件的内容,如果大量的文件时间被修改,但是内容没有变,这样的同步可能显示效率低下无意义,浪费带宽和时间;

第二: 更常见的是在程序编译中,由于文件的时间被修改了,系统需要重新把这些文件编译,对于特别的大的工程,一个文件的修改会导致依赖文件都要重新编译,这会导致程序编译时间边长,浪费资源和时间。

因此我们希望当文件内容没有修改的时候,不要强制写入文件,有修改的时候才写入,vim 提供的 :x (exit) 就能符合这种要求,所以 :x 是更好的保存退出 vim 的方式。

如果希望把文件另存为可以使用 :w 3.txt 或 :wq 3.txt 的方式,使用 :w 还可以选择要保存的行到另外的文件,:2,10 w 2-10.txt 表示把第2行到10行保存到文件 2-10.txt 中,: 后跟数字在 vim 里一般表示行数,比如输入 :2 会让当前光标跳转到第 2 行,在以后的章节还会详细讲解。

你可能会看到 :qa!,它用于多窗口的时候退出所有的缓冲区并且放弃修改,目前还没有讲到 vim 的多窗口模式,暂且了解。

如果在编辑过程中,想放弃上次 :w 之后的修改,可以使用 :e! 命令丢弃修改,如果打开文件后没有执行过 :w,那 :e! 相当于恢复到打开文件的初始状态。

执行 :e! 后,最近的修改 wronge somthing 将被丢弃,继续 :q 退出 vim,就像文件从未被打开过一样。

05-配置文件和插件

我们尝试用 vim 编辑一个简单的 c 语言文件,vim main.c,输入以下内容:

int main() {
    return 0;
}

不懂 c 语言,没事,放心这个教程不讲 c 语言。默认的话,这段 c 代码的关键字应该是语法高亮的,如果没有的话,esc 回到命令模式,输入 :syntax on 后立即高亮。

我们继续给它加一个现实行号的功能,输入 :set number 或 :set nu,行号 OK 了。

使用 :wq 退出后,重新 vim main.c 打开,发现刚才设置的行号没了,于是我们重新 :set nu,好麻烦,对不对,有没有办法启动 vim 的时候自动设置上行号呢? 有,那就是 ~/.vimrc 文件,这个文件可能不存在,让我们新建一个,输入 vim ~/.vimrc,按 a 键进入编辑模式,把刚才的 set number 或者 set nu 写进去,:wq 保存退出。

现在我们来打开 main.c 文件,vim main.c,是不是默认就显示行号了,如果你的 vim 打开默认没有语法高亮,在 ~/.vimrc 文件里把 syntax on 也加上。也就是说 ~/.vimrc 文件是当前用户的 vim 启动配置文件,vim 启动的时候会去读取该文件,载入一些预定义的配置。

不过有个问题,我们之前没有配置语法高亮,为什么第一次打开,代码里的关键字也高亮了呢,高亮开启是内置到 vim 这个软件了吗? 这是因为 vim 还有全局配置文件,它是 /etc/vim/vimrc,这个配置对所有用户生效,看看它是否设置了语法高亮。

现在明白了,我们可以把 set nu 也加到这个文件里,这样所有的用户启动 vim 的时候都可以默认显示行号了,试试看。先删除我们自己用户下的 ~/.vimrc 文件,接着 sudo vim /etc/vim/vimrc 打开,我们按大写的 G 键跳转到文件的最末尾一行,接着按 o 键新增一行并且进入编辑模式,输入 set nu,按 esc 后 :wq 保存退出。

现在 vim main.c,文件也显示行号了,说明在 /etc/vim/vimrc 文件中添加的 set nu 生效了。不过通常情况下,我们为了不影响其他用户,还是会把配置加到自己的 ~/.vimrc 文件中,而且 /etc/vim/vimrc 普通用户无法编辑(这是上面输入 sudo 的原因)。

如果在配置文件中写入了错误的指令会怎么样? 我们加一个 notfound 在 ~/.vimrc 里。

再次启动 vim,提示错误,并且按 enter 继续,顺利进入 vim,所以不要怕,设置错了也没事,随便折腾。

wangbo@wangbo-VirtualBox:~/test/vim$ vim
处理 /home/wangbo/.vimrc 时发生错误:
第    4 行:
E492: 不是编辑器的命令: notfound
请按 ENTER 或其它命令继续

好了,除了语法高亮和显示行号以外,我们需要了解一下其他的指令。

set nocompatible " 使用 vim 的强大功能,不要 vi 兼容
set encoding=utf8 " 默认的文件编码

set tabstop=2 " 一个 tab 键显示 2 个空格
set softtabstop=2 " 把 tab 键转换成空格
set shiftwidth=2 " 自动缩进的字符宽度
set expandtab " 配合 softtabstop 使用
set showmatch " 高亮对应的括号
set showmode " 显示当前模式
set showcmd " 显示命令
set ignorecase " 搜搜忽略大小写
set hlsearch " 高亮搜索结果
set incsearch " 即时高亮符合搜索的第一个内容
set nowrap " set wrap 换行
set nu "set nonu 不显示行号
set cc=80 " 在 80 列显示红线,以免写的列太宽不好看

你可以把这些指令根据需要加入到 ~/.vimrc 文件中,或者需要的时候在用 : 的底线命令模式来临时执行一次。

如果需要给 vim 加上红蓝 buf,就需要用到它的插件模式,网上有无穷无尽的 vim 插件来实现各种五花八门的功能。使用 vim 插件之前,需要在配置文件开启插件。但是我们通常不直接配置 vim 的插件,会使用 vim 的插件管理器,比如 Vundle、vim-plug 等等。以 Vundle 为例,需要先下载 Vundle 把他放到 ~/.vim/bundle/Vundle.vim,如果你稍微懂 git 可以使用 git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim 直接克隆下来,不会也没关系,后面会有 git 的教程open in new window,你直接去 https://github.com/VundleVim/Vundle.vim/releasesopen in new window 下载一个下来放到这个目录就行了 。

打开 ~/.vimrc 加入配置:

" ----------------------------- Vundle Start -----------------------------
set nocompatible
filetype off
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'

" 引入插件的申明

call vundle#end()
filetype plugin indent on
" ----------------------------- Vundle End   -----------------------------

wq 保存一下,重启 vim,如果没有发生错误,说明 Vundle 已经配置好了。

接下来就是找你需要的插件来加入申明了,我们发现这个件插件挺帅的 https://github.com/vim-airline/vim-airlineopen in new window,干他。

在 ~/.vimrc 的 " 引入插件的申明下面一行里加入:

只是申明还不能使用,我们需要通知 vim 去下载这些插件的文件,先 :w 保存一下配置文件,然后底线命令模式 :PluginInstall 执行。

vim 显示 Processing 'vim-airline/vim-airline' 等就开始去下载安装插件了。

底部显示 Done! 就说说明安装完成了,输入 :qa 退出 vim,接着 vim main.c 看看,底部多了状态栏,显示了文件的信息和当前光标在文件中的进度,还有行号和列号,帅成 P 了有没有。

什么不够帅,好吧这只是默认配置,去看看别人的配置充值一下信仰 https://github.com/vim-airline/vim-airline/wiki/Screenshotsopen in new window,而且这个插件只是 vim 插件这片大海的一滴水而已,很多的插件你以后会碰到的,不过安装方法都是一样的,每个插件有一些参数配置,注意阅读插件的文档。

这个网站 https://vimawesome.com/ 统计了 vim 插件的排行榜,可以作为参考。

至此,你应该知道怎么配置 vim 和增强 vim 了。一个小作业,安装一下这个插件 https://github.com/mg979/vim-visual-multiopen in new window,它提供了 Sublime Text/VS Code 中 Ctrl+d 的批量编辑功能。

06-文本穿梭的绝技凌波微步

终于到这一章了,通过上面的章节你已经有了很强大的 vim,但是写起内容来依旧很吃力,常常搞得一团乱,为什么? 因为没有鼠标产生了恐惧感,别着急 vim 天生为键盘而生,有一箩筐的功夫来秒杀鼠标,本章节就教你 vim 的凌波微步第一层,对,只学到第一层就足够用了。

在使用高效的移动之前,首先我们要调节一下键盘的设置,以我的 mac 为例,打开键盘设置把按键和重复调节到最快,按键的延迟调节到最短,这样我们在使用 vim 的命令模式的时候才能迸发出速度。

现在你按 hjkl 可以在每个字符中间上下左右快速的移动了,因为 hjkl 只能在单个字符之间移动,所以当我们在文档之间移动相当大的距离的时候,效率会比较低下,怎么高效的移动呢? 首先要对移动进行分类:

大段移动

由于在编辑文档的时候通常会显示行号,所以在修改文档的时候最常想到的就是在行之间跳转,比如输入 :10,光标就能跳转到第 10 行,:0 和 :1 会跳转到第一行,对于一个长文档来说记住最后一行的行号是不太现实的,所以 😒 用于跳转到最后一行,不喜欢使用底线命令模式,也可以在普通命令模式下输入 10G 或者 10gg 到第 10 行,而 gg、1G 会跳转到第一行,G 会跳转到最后一行,这些都是绝对行移动,作为相对移动,2$ 会在当前行的基础之上移动行后 2 行的行尾。

对于当前屏幕内的文本,H 可跳转到顶部,M 跳转到中间,L 跳转到底部,ctrl + d 向下跳过半屏,ctrl + u 向上跳过半屏。

( 移动光标到上一句,) 移动光标到下一句,句号分开为准。

{ 前移一段,} 后移一段,空行分开为准。

行内移动

$ 在 vim 里经常表示结束、尾巴的意思,如果你在行内直接按 $ 会跳转到行尾,与此相反 ^ 跳转到行首,而 w 可以按照单词移动,它会移动到每个单词的第一个字母,b 也能移动到单词的第一个字母,但它是反向移动。e 则移动到下一个单词的末尾,它的反向移动是 ge。

如果想定位到一个字符可使用 f 或者 F 指令,比如 fx 能定位到下一个 x 字符,F 则为反方向。t 和 T 也是做类似的事情,但是 tx 定位到 x 的前一个字符,Tx 反向定位到 x 的后一个字符。同样的,在这些命令前都可以加一个数字 n,表示符合条件的第 n 个。

使用这些命令有个技巧,可使用 ; 和 , 来重复执行,比如输入 fe 跳转到第一个 e 后,再次执行 ; 则会定位到下一个 e 字符位置,只能在行内移动。

搜索定位

% 可定位到另一个匹配的括号位置 ( ) { } [ ],你会发现默认不能跳转和高亮尖括号<>,需要在 ~/.vimrc 里增加设置 set mps+=<:> 即可,操作的时候如果光标不在括号上会先正向查找一个括号,再此按 % 再跳转到对应的括号。/ 和 ? 可搜索,按 n(正) 和 N(反) 可在两个方向跳转到符合条件的下一个。# 和 * 可以立刻搜索当前光标所在的单词,它们两个方向相反,如果打开 set hlsearch 和 set incsearch 看不出区别。

在跳转的过程中,如果想回到跳转之前的光标位置,可以按 ` 两次,也就是 `` (键盘左上角,不是单引号),重复按就能在 2 个位置跳来跳去。

滚动屏幕

zz 可以把当前行移动到屏幕中央,zt 移动到顶部(top),zb 移动到底部(bottom)。

ctrl + f 滚动一屏(forward),ctrl + b 反方向滚动一屏(back)。

ctrl + e 向前滚动一行,ctrl + y 向后滚动一行。

07-新增和修改内容

在前面的章节已经接触过 aio/AIO,它们都立刻进入新增文本的编辑模式,总结一下他们的区别:

  1. a/A 都是新增,a 是在光标之后,A在行尾后增加。
  2. i/I 都是插入,i 是在光标当前位开始插入,I 则在行首进入插入。
  3. o 是在当前行的下一行新增,O 则在当前行的上一行新增。

除了新增的操作,最常见的往往是修改操作。x、X 用于删除单个字符,X 删除光标前一个字符,它们删除后 vim 仍然处于命令模式,类似的前面可以跟一个数字表示删除几个字符,比如 10x 删除光标所在的 10 个字符。在 vim 里,p 是粘贴的意思,而 x、X 删除字符后会把字符放入粘贴缓冲区,按 p 会粘贴到光标所在处,并且仍然处于命令模式,xp 可以用于交换 2 个字符。

如果需要删除多个字符,连续使用 x、X 可能效率不够高,所以需要知道更多的删除方法。如果直接删除一个单词,可以直接使用 dw,类似的 d2w 会连续删除 2 个单词。和 dw 类似有 de,它们的区别是如何对待单词后面的空格,dw 会把空格一并删除掉,de 会保留单词后的空格。dd 用于删除一整行,2dd 连续删除 2 行,而 :2,4d 删除第 2 行到第 4 行。

由于 0、^ 有首部,$ 有尾部的含义,所以 d0 可以删除当前光标到行首,d$ 可以删除当前光标到行尾,D 也可以实现同样的效果。

而 1G、gg 有文档首部,G 有文档尾部的含义,所以 d1G、dgg 可以删除当前光标到文档开头,dG 可以删除当前光标到文档尾部。

交换 2 行是写代码的常用操作,dd 删除一行后存在粘贴缓冲区,p 用于粘贴,合起来 ddp 就可以实现交换 2 行的操作。

删除的时候常常会接着修改,如果删除以后仍然处理命令行,进入编辑模式需要按 a、i,所以删除后直接进入编辑模式可能会更加方便。

cw 可以删除一个单词,和 dw 不同的是它删除后马上开始编辑,类似的同样有 c2w、c3w 等。

cc、S 删除一整行并且理解进入编辑模式,相当于执行 ddO 的效果。

R 比较有意思,按了之后进入了替换编辑模式,可以一直输入替换当前光标的字符,直到按 esc 结束,替换的过程中你还能按退格键恢复。

~ 可以进入大小写切换模式,大写转换成小写,小写转换成大写,r 用于替换当前光标下的单个字符。

搜索替换也是经常使用的操作,😒/from/to 用于把当前行的 from 替换成 to,如果后面加 /g,则会替换当前行所有的 from,如果以 :%s/ 开始就表示搜索范围是整个文档。以 :10s/ 开头可以指定第 10 行,:$s/ 表示最后一行,:2,10s 表示第 2 到 10 行,输入行号可能不是那么的快捷,使用 shift + v 进入可视模式后,再按 : 进入底线命令模式,可以把选择的行号自己填充进去,只需要输入后面的命令回车即可。

多个行同一列进行批量编辑非常有用,ctrl + v 进入列编辑模式,它的使用方法比较特别,比如要给1~5 行最前面都加入一个 # 再全部删除 # 的操作:

  1. 按 ctrl +v 后按 j 选择至第 5 行 (还可以按方向 l 键选择更多的列)
  2. 按 I 进入插入模式输入 # 按 esc 第一行加上了 #
  3. 再按 esc 后 2~5 行后加上了 #
  4. 再次 ctrl + v 后按 j 选择至第 5 行
  5. 按 x 后可以删除所有的 #

写代码的时候经常需要对齐格式化,先用 shift + v 进入可视模式,选择待格式化的行后按 =,而 == 可以立刻格式化当前行,<< 和 >> 用于用于缩进调整。

最后,. 可以重复上一个命令操作,比如输入 dd 后,按 . 会一直连续删除行,但它不是对所有的命令都生效。而撤销和重做在 vim 里使用 u、ctrl + r 来实现,U 的作用是撤销最近编辑那行的所有操作。

08-复制粘贴

前面已经使用过 xp 和 dpp 来交换了,本章节再来温故一下 vim 的复制粘贴如何使用。首先复制一行是 yy 指令,粘贴是 p、P 指令,p 是粘贴到当前行的下一行,P 是粘贴到当前行的上一行,粘贴之后仍然处于命令模式。 根据前面的学习已经有经验了,能够推导出一些用法,比如 2yy、y1G、ygg、yG、yw、y2w、:2,3y,而 y0 可以复制光标到行首,y$ 复制光标到行尾。 使用 vim 的可视化模式后进行 y 复制会更加方便。

单按 v、shift+v 进入字符可视模式,可使用 h 和 l 方向键扩大选择范围,按 y 后可视模式提示消失,可以用 p、P 粘贴了。

因为你已经有很多 vim 的经验了,可以自己学习很多命令,本章节就不赘述了,如果不懂的话,就是 :help yy 看看帮助。

09-暂时性的离开 vim

由于我们常常在 shell 里工作,启动 vim 编辑的时候,可能还需要做一些其他的工作,但是编辑的工作并没有完成,所以我们其实不想真正的关闭 vim,vim 提供了一些支持。 首先可以使用 :! 来执行 shell 命令,比如编辑的时候突然需要查看一下某个目录下的文件列表,我们可以输入 :!ls -l 试试。

回车执行后,vim 显示文件列表,并且提示 "请按 ENTER 或其它命令继续",按 enter 后又回到了 vim 的界面,并且 vim 里的环境依然保持。 如果我们需要连续的执行多次领命,可能每次去输入 :! 就不是很方便了,我们可以让 vim 直接创建一个 shell,输入 :shell 新开一个 shell 环境,使用完毕后 exit 回到 vim 的环境。 因为 vim 也是一个 linux 命令,所以我们依然可使用 ctrl + z 把它挂起,然后使用 fg 恢复。

[1]+  Stopped(SIGTSTP)        vim poem.txt
wangbo@wangbo-VirtualBox:~/test/vim$ jobs
[1]+  Stopped(SIGTSTP)        vim poem.txt
wangbo@wangbo-VirtualBox:~/test/vim$ fg

如果我们要注销或者关机、重启,仍然想保持 vim 的工作状态怎么办? 这个时候需要把 vim 的工作环境保存成一个文件,使用 :mksession 命令,我们先复制 3 行,然后执行 :mksession sess.vim 和 :q 退出 vim,发现磁盘上多了一个 sess.vim 文件,用 file 甄别说是一个 vim 数据文件。

wangbo@wangbo-VirtualBox:~/test/vim$ ls -l
total 32
-rw-rw-r-- 1 wangbo wangbo   39 1217 14:03 1.txt
-rw-rw-r-- 1 wangbo wangbo   15 1217 14:28 2.txt
-rw-rw-r-- 1 wangbo wangbo   23 1216 16:43 hello.txt
-rw-rw-r-- 1 wangbo wangbo   58 1220 16:30 main.c
-rw-rw-r-- 1 wangbo wangbo  580 1220 17:04 poem.txt
-rw-rw-r-- 1 wangbo wangbo   76 1218 10:03 print.c
-rw-rw-r-- 1 wangbo wangbo 5043 1220 17:23 sess.vim
wangbo@wangbo-VirtualBox:~/test/vim$ file sess.vim
sess.vim: data

下次怎么恢复呢? 我们可以直接运行 vim -S sess.vim,也可以 vim 进入后,敲 :source sess.vim 执行一次。

还记得我们复制过 3 行文本吗? 进去直接输入 p 命令,发现文本被粘贴上了。说明之前的 vim 工作状态被恢复了。

10-分屏和切换窗口

前面讲过 vim 支持多窗口、分屏操作,本章就来学习一下。首先输入 vim -on 1.txt 2.txt 和 vim -on 1.txt 2.txt 看看有什么不同。

发现这两个命令的窗口排列方式不同,一个是按行排列的,一个按列排列的。用 qa 退出后,我们输入 vim 1.txt,然后 :sp 2.txt 发现窗口也是按行排列的,这里的 sp 是 split,也就是分割窗口的意思,如果要按列排列窗口可以输入 :vsp 2.txt。

如果想直接分割窗口怎么办? 直接输入 new、vnew 就可以,也可以使用 ctrl + w + s、ctrl + w + v。窗口可以无限分割下去,当然这并没有什么太大的意思,一般 2 个窗口就足够用了。

有了窗口以后第一个麻烦就是,如何在窗口之间跳转呢?

第一个办法是使用 ctrl + w 配合 hjkl 方向键来使用。

第二个办法是使用 ctrl + w w 依次在窗口之间切换。

:close 可以关掉当前窗口,:only 可以关闭除当前窗口的其他窗口。

Last Updated:
Contributors: Bob Wang