简介
什么是vim?
Linux下两大编辑神器之一 vim
- Linux/Unix下使用最多的编辑器
- vi的改进版
- 可能是最难上手的编辑器之一
为什么要学习vim?
都21世纪了,为什么还需要学习vim?
- 只要你用服务器,vim就是个必备技能
- 后端和运维工程师必须掌握的编辑器,不然服务器上操作很尴尬
- 可以纯键盘编辑,提升编辑效率
初识vim
据说Stackoverflow帮助过上百万开发者退出过vim!
- 输入vim命令进入vim
- 进去之后似乎没法输入东西,起始默认进去是normal模式
- 使用:q可以退出vim
开始编辑
进入编辑模式就可以像其他编辑器一样编辑了
- 使用i(insert)进入编辑模式,开始输入文字
- i表示(insert),a(append),o(open a line below)
- 使用Esc可以退出编辑模式,又回到normal模式。
- 使用:wq保存退出
w(write),q(quit)
insert -> normal:使用Esc
normal -> insert:(小写是操作字符,大写是操作行)
- i:(insert)表示进入insert编辑模式后,在当前字符的前面插入
insert
- a:(append)表示进入insert编辑模式后,在当前字符后附加插入
append
- o:(open a line below)表示进入insert编辑模式后,在当前行下新启一行插入
append
- I:(insert before line)表示在当前行的最前面插入
insert before line
- A:(append after line)表示在当前行的最后附加插入
append after line
- O:(append a line above)表示在当前行上新启一行插入
append a line above
保存:
- :w:(write)写入,保存
write
- :q:(quit)退出
quit
- :wq:(write and quit)保存并退出
write and quit
vim,为什么你有这么多模式
怪异的vim模式
vim和其他很多编辑器的区别在于多种模式
- 进入vim默认是normal(普通)模式?为什么不是编辑模式呢?
- 使用a(append)、i(insert)等进入编辑模式
- 还有cmd命令模式和v(visual)可视化模式
为什么进入vim不能插入-normal模式
奇怪的是,为什么vim进入之后不像其他编辑器一样直接插入?
- 进入Vim默认是normal(普通)模式。使用Esc从插入回到普通模式
- 普通模式下可以进行各种命令操作和移动
- 大部分情况下你是在浏览而不是编辑,所以Vim默认是normal
Insert(插入)模式
插入模式下Vim可以直接编辑,和其他编辑器一样
- 使用i(insert) 、a(append) 、o (open a line below)进入插入模式
- 使用Esc退出插入模式到normal模式
Command(命令)模式
Normal模式下输入:之后执行命令,比如保存退出:wq一气呵成
- 顾名思义,执行Vim命令,比如保存:w,退出:q
- 比如分屏::vs (vertical split),:sp(split)
- 比如使用::% s/foo/bar/g全局替换
%:表示本文件全部文本 g:表示全局替换
Visual(可视)模式
Visual模式一般用来块状选择文本(类比使用Alt+鼠标拖动)
- Normal模式下使用v进入visual选择
- 使用V选择行
- 使用ctrl+v进行方块选择
vim编辑小技巧
如何快速纠错(insert模式下)
代码就像人生,总是经常出错,需要我们快速修正
- 进入Vim之后首先使用a/i/o之一进入插入模式
- ctrl+h 删除上一个字符,ctrl+w删除上一个单词,ctrl+u删除当前行
快速切换insert和normal模式
很多键盘的键位设置, Esc按起来不方便,不方便切换到normal
- 使用Ctrl+c代替Esc (但是可能会中断某些插件)或者ctrl+[
- gi 快速跳转到你最后一次编辑的地方并进入插入模式
- 在配置章节我们会讲解如何映射方便的快捷键来替代Esc
vim快速移动大法
反人类的hjkl
众所周知,vim normal模式使用了反人类的hjkl来移动
- 据说编辑器作者在编写vim的时候键盘还没流行上下左右按键
- h(左) ,j(下) ,k(上) ,l(右) ,移动也不会让手指脱离主键盘区
- 相信我,逼着自己使用hjkl一段时间之后,你会体会到它非常有效率
使用hjkl
只需要几天的适应,你就可以熟练使用hjkl来移动
在单词之间“飞舞”(normal模式下)
在单词之间移动是我们浏览操作中最常使用的命令
- w/W移到下一个word/WORD开头。e/E 下一个word/WORD尾
- b/B 回到上一个word/WORD开头,可以理解为backword
- word指的是以非空白符分割的单词, WORD以空白符分割的单词
行间搜索移动
同一行快速移动的方式其实是搜索一个字符并且移动到该字符
- 使用f{char}可以移动到char字符上,t{char}移动到char的前一个字符
f:(find) t:(until) 可以配合删除使用,如 dt)从当前字符开始删除直到)。
- 如果第一次没搜到,可以用分号(;)/逗号(,)继续搜该行下一个/上一个
- 大写的F{char}表示反过来搜前面的字符
vim水平移动
如何快速移动到一行的行首或者行尾呢?
- 0移动到行首第一个字符,^移动到第一个非空白字符
^:在正则表达式中,表示行的开头
- $移动到行尾,g_ 移动到行尾非空白字符
$:在正则表达式中,表示行的结尾
- 记住常用的0和$就可以满足日常需求了
Vim垂直移动
我们还可以在句子(sentence)和段落(paragraph)间移动(不常用)
- 使用括号()在句子间移动,你可以用:help (来查看帮助
- 使用{}在段落之间移动
- 插件章节我们会讲如何用easy-motion插件移动,这些命令就都不用记忆啦
Vim页面移动
翻页命令太多, 2/8定律,记住常见的就能满足需求
- gg/G移动到文件开头和结尾,你可以使用ctrl+o快速返回
- nG移动到文件第n行,可以配合命令使用,如 dnG表示删除到第n行
- H/M/L跳转到屏幕的开头(Head),中间(Middle)和结尾(Lower)
- Ctrl+u,ctrl+f上下翻页(upword/forward)。zz 把屏幕置为中间
什么是vim的text object
Text Object(文本对象)
如果你学过主流的编程语言,一定知道面向对象编程
- Vim里文本也有对象的概念,比如一个单词,一段句子,一个段落
- 很多其他编辑器经常只能操作单个字符来修改文本,比较低效
- 通过操作文本对象来修改要比只操作单个字符高效
文本对象操作方式
之前我们已经使用过文本对象了,回忆下dw (删除一个单词)
- [number]
[text object] - 或者
[number] [text object] - number表示次数,command是命令,d(delete),c(change),y(yank)
- text object是要操作的文本对象
比如单词w(word),句子s(sentence),段落p(paragraph),标签t(tag)
通过示例来学习
- iw表示inner word,如果键入viw命令,那么首先v将进入选择模式,然后iw将选中当前单词。
- aw表示around word,它不但会选中当前单词,还会包含当前单词之后的空格。
以下实例中的**[]表示作用范围**:
- iwThis is a [test] sentence.
- awThis is a [test]sentence.
- iwThis is a [...test...] sentence.
- awThis is a [...test...]sentence.
- is...sentence. [This is a sentence.] This...
- as...sentence. [This is a sentence.]This...End of previous paragraph.
- ip[This is a paragraph. It has two sentences.]The next.End of previous paragraph.
- ap[This is a paragraph. It has two sentences.]The next.
- ip
[hello world] - ap[
hello world ] - i( or i)1*([2+3])
- a( or a)1*[(2+3)]
- i< or i>The <[**tag**]>
- a< or i>The [
] - i{ or i}some {[code block]}
- a{ or a}some [{code block}]
- i[ or i]some [[code block]]
- a[ or a]some [[code block]]
- i"The "[best]"
- a"The ["best"]
- i'The '[best]'
- a'The ['best']
vim快速增删改查
数字+命令表示多次执行命令,如 2dw
或者 命令+数字+对象,如 d2w
Vim增加字符
增加字符就是进入插入模式,开始敲键盘吧
- 进入插入模式编辑文本
- 使用a/i/o进入插入模式
- 还记得A/I/O是什么作用么?
Vim快速删除
如何快速删除一个字符或者单词呢 ?
- Vim在normal模式下使用x快速删除一个字符
- 使用d (delete)配合文本对象快速删除一个单词 daw (delete around word)
dw:默认为daw
- d和x都可以搭配数字来执行多次
Vim快速修改
相比删除,更常用修改,一般是删除之后改成我们期望的文本
- normal 模式下,常用有三个, r (replace),c (change),s(substitute)
- 使用r可以替换一个字符。
- 使用s替换并进入插入模式
- 使用c配合文本对象,我们可以快速进行修改整个文本对象。如ci"、ciw
Vim查询
查询单词也是一个常用操作
- 使用/或者?进行前向或者反向搜索
- 使用n/N跳转到下一个或者上一个匹配
next
- 使用*或者#进行当前单词的前向和后向匹配
*:在正则表达式中表示出现0次或多次。
vim如何搜索替换
Vim替换命令
substitute命令允许我们查找并且替换掉文本,并且支持正则式
- :[range]s(substitute)/{pattern}/{string}/[flags]
- range:表示范围。比如:10,20表示10-20行,%表示本文件中所有的行。
- pattern:是要替换的模式,正则表达式
- string:是替换后文本
注意:此命令不仅能用来替换,还能用来删除!如 删除所有星号,命令为:%s/\//g
替换范围range
你可以在命令之前设置一个范围。以下是一些方法:
- m, n:表示范围为从第 m 行到第 n 行
- 1,:表示范围为从第 1 行到当前行
注意:第一行的行号是已知的,行号为 1;而最后一行的行号是未知的,所以我们只用给最后一行分配一个符号代表即可。
除了数字之外,还可以使用这些符号作为范围:
- .:表示当前行,可以省略。如 .,3表示当前行到第 3 行。
- $:表示文件中的最后一行。如 3,$表示第 3 行到最后一行。
- +n:表示当前行的后 n 行。3,+1 或者 3,.+1,.可以用或不用,表示第 3 行到当前行后的 1 行。
- %:表示本文件中所有的行。
在 Vim 中,%通常表示整个文件。
示例:
- :1,5g/console/d 匹配第 1 行和第 5 行之间的字符串"console"并删除它们。
- :,5g/console/d 如果逗号之前没有地址,则从当前行开始。它在当前行和行 5 之间寻找字符串"console"并删除它们。
- :3,g/console/d 如果逗号后没有地址,则以当前行结束。它寻找第 3 行和当前行之间的字符串"console"并删除它们。
- :3g/console/d 如果仅传递一个没有逗号的地址,则它只执行第 3 行的命令。它查看第 3 行,如果字符串"console",它将删除它。
替换模式pattern
请参照正则表达式
替换标志位flag
Flags有几个常用的标志
- 默认为空,表示仅替换一行上的第一个匹配项
- g(global) 表示改变一行上的每个符合条件的匹配项,全局范围内执行
global
- c(confirm)表示确认 ,可以确认或者拒绝修改
confirm
- n(number)报告匹配到的次数而不替换,可以用来查询匹配次数
number
& Reuse the flags from the previous substitute command. Must be passed as the first flag.g Replace all matches in the line.c Ask for substitution confirmation.e Prevent error message from displaying when substitution fails.i Perform case insensitive substitutionI Perform case sensitive substitutionThere are more flags that I do not list above. To read about all the flags, check out .:h s_flags
Vim如何对匹配行执行命令
global命令实际上是分成两步执行:首先扫描[range]指定范围内的所有行,给匹配{pattern}的行打上标记;然后依次对打有标记的行执行{command}命令,如果被标记的行在对之前匹配行的命令操作中被删除、移动或合并,则其标记自动消失,而不对该行执行{command}命令。{command}可以是一个ex命令,也可以是用|分隔的多个ex命令,这样我们就可以对被标记行,或从标记行寻址到的行进行多种不同的操作。
全局命令执行步骤:
- 扫描[range]指定范围内的所有行,给匹配{pattern}的行打上标记;
- 依次对打有标记的行执行{command}命令,如果被标记的行在对之前匹配行的命令操作中被删除、移动或合并,则其标记自动消失,而不对该行执行{command}命令。
全局命令(The Global Command)
Vim 的全局命令用于同时在多行上运行命令行命令。
特别注意:全局命令是对匹配的行,来进行行操作,操作对象是整个行,而不仅仅是匹配的那一部分字符。
全局命令具有以下语法:
- :[range]g(global)/{pattern}/{command}该模式匹配包含该模式的所有行,类似于 substitute 命令中的模式。该命令可以是任何命令行命令。全局命令的工作方式是对匹配pattern的每一行执行command命令。
反向全局命令
要在不匹配的行上运行全局命令,可以运行:
- :[range]g(global)!/{pattern}/{command}
- 或:v/{pattern}/{command}
通过示例来学习
如果你有下列表达式:
const one = 1;console.log("one: ", one);const two = 2;console.log("two: ", two);const three = 3;console.log("three: ", three);
要删除所有包含“控制台”的行,您可以运行::g/console/d
结果:
const one = 1;const two = 2;const three = 3;
如果使用反向全局命令,则它将删除所有不包含“console”的行
vim如何编辑命令(如 粘贴一个命令)
问题速成解决方式
怎样在vim的命令行中拷贝,粘贴?
在vim的普通模式下,输入:进入命令行模式,我们可以在屏幕下方输入我们想让vim执行的命令。
有时候,我们需要在命令行中执行拷贝粘贴,例如 我们想要查找替换正文中的特殊字符,但我们又不知道怎么在命令行中输入这个特殊字符。
查找还好说,不一定非要在命令行中输入这个特殊字符,只要把光标放在这个特殊字符上,按*向下查找,按#向上查找。但是如果我们要替换这个特殊字符,就必须在命令行中输入它了,但是又不知道怎么输入这个特殊字符,怎么办?答案是拷贝粘贴,先在正文中复制这个字符,然后粘贴在命令行中。可是怎么在命令行中实现粘贴呢?别急,介绍一个好东西:命令行窗口。
在正常模式下,输入q:,就打开了命令行窗口。命令行窗口中都是我们输入过的命令,在这个窗口中我们可以像编辑正文一样编辑新的命令,或修改旧的命令。因为像编辑普通缓冲区一样,自然可以拷贝粘贴了。编辑完之后,在想要执行的命令上按回车,就ok了。命令执行后,命令行窗口也自动关闭了。命令行窗口
“历史记录”窗口显示以前使用的命令行命令的列表,但是不能从“历史记录”窗口执行该命令。若要在浏览时执行命令,请使用命令行窗口。有三个不同的命令行窗口:
q: Command-line windowq/ Forward search windowq? Backward search window
q(quick,快速的):
- q::命令行命令编辑,可以看到所有使用过的命令的历史记录
- q/:向前搜索命令编辑,可以看到所有使用过的向前搜索命令的历史记录
- q?:向后搜索命令编辑,可以看到所有使用过的向后搜索命令的历史记录
运行 q: 以启动命令行命令的命令行窗口。Vim 将在屏幕底部启动一个新窗口。您可以使用 Up 或 Ctrl-p 键向上遍历,使用 Down 或 Ctrl-n 键向下遍历。如果按下回车,Vim 将执行该命令。若要退出命令行窗口,请按 Ctrl-c、 Ctrl-w c 或键入: quit。
类似地,要启动用于搜索的命令行窗口,可以运行向前搜索的 q/ 和向后搜索的 q?。
vim复制粘贴与寄存器的使用
Vim Normal模式复制粘贴
初学者会感觉Vim复制粘贴比较奇怪,先从normal模式学习
- normal模式下复制粘贴分别使用y(yank)和p(put) ,剪贴d(delete)和p(put)
- 我们可以使用v(visual)命令选中所要复制的地方,然后使用p粘贴
- 配合文本对象:比如使用yiw复制一个单词, yy复制一行Vim里的剪贴(Cut)复制(copy)粘贴(paste)分别是delete/yank/put
Insert模式下的复制粘贴
很多人会使用鼠标进行选中,然后使用ctrl+v或者cmd+v粘贴
- 这个和其他的文本编辑器差不多,但是粘贴代码有个坑
- 很多人在vimrc中设置了autoindent ,粘贴Python代码缩进错乱这个时候需要使用:set paste和:set nopaste解决
- Ctrl-r {register}:可以粘贴寄存器{register}中的内容。
系统剪贴板寄存器为 "+
什么是Vim的寄存器?
你有没有好奇? Vim在normal模式下复制/剪贴的内容去了哪?
- Vim里操作的是寄存器而不是系统剪贴板,这和其他编辑器不同
- 默认我们使用d删除或者y复制的内容都放到了“无名寄存器”
- 用x删除一个字符放到无名寄存器,然后p粘贴,可以调换俩字符
深入寄存器(register)
Vim不使用单一剪贴板进行剪贴、复制与粘贴,而是多组寄存器
- 通过"{register} 前缀可以指定寄存器,不指定默认用无名寄存器
- 比如使用"ayiw复制一个单词到寄存器a中,"bdd删除当前行到寄存器b中
- Vim中""表示无名寄存器,缺省使用。""p其实就等同p
其他常见寄存器
除了有名寄存器a-z, Vim中还有一些其他常见寄存器
- 复制专用寄存器"0:使用y复制文本同时会被拷到复制寄存器0
- 系统剪贴板 "+:可以在复制前加上 "+ 复制到系统剪贴板
- 其他一些寄存器比如 "%:当前文件名,".:上次插入的文本
:set clipboard=unnamed:可以让你直接复制粘贴系统剪贴板内容
强大的vim宏(macro)
从需求说起
从一个需求说起,给多行url链接加上双引号,你会怎么做呢?
什么是Vim宏(macro)
很多使用Vim很久的Vimer都没用过宏
- 宏可以看成是一系列命令的集合
- 我们可以使用宏「录制」一系列操作,然后用于「回放」
- 宏可以非常方便地把一系列命令用在多行文本上
如何使用宏
宏的使用分为录制和回放,是不是有种拍电影的感觉
- Vim使用q{register}来录制,同时也是q结束录制
quick
- 使用q{register}选择要保存的寄存器,把录制的命令保存其中
- 使用q结束录制
- 使用@{register}回放寄存器中保存的一系列命令
用宏解决刚才的问题
现在你知道宏怎么用了,如何解决刚才的问题呢?
- 先给一行加上双引号,然后再回放到其他所有行
- 我们先使用q{register}开始录制,给一行加上双引号,之后使用q退出
- 在剩下的所有行中回放录制的宏(使用可视模式选中,然后使用宏)
需要使用v可视模式选择所有url,再使用:normal在命令模式下使用normal模式下的命令。
不使用宏解决刚才的问题
可以通过可视模式选择所有url
可以使用:normal,在命令模式使用normal模式的命令:normal I":normal A"vim补全大法
什么是补全?
只要手速快,补全什么的都是浮云
- 补全是根据当前环境上下文由编辑器“猜”你想输入的东西
- 比如补全一个单词、文件名,或者代码中的函数名、变量名等
- Vim中提供了多种补全功能,还可以由插件拓展功能实现代码补全
vim中常用的补全
Vim中有很多种补全的方式
命令 | 补全类型 |
---|---|
<C-n> | 普通关键字 |
<C-x><C-n> | 当前缓冲区关键字 |
<C-x><C-i> | 包含文件关键字 |
<C-x><C-]> | 标签文件关键字 |
<C-x><C-k> | 字典查找 |
<C-x><C-l> | 整行补全 |
<C-x><C-f> | 文件名补全 |
<C-x><C-o> | 全能(Omni)补全 |
常见的三种补全类型
记不住不用担心,根据笔者经验,最常用的就三种
- 使用ctrl+n和ctrl+p补全单词
- 使用ctrl+x和ctrl+f补全文件名
- 使用ctrl+x和ctrl+o补全代码,需要开启文件类型检查,安装插件
vim多文件操作
Buffer Window Tab
我们先来介绍几个多文件操作相关的概念
- Buffer是指打开的一个文件的内存缓冲区
- 窗口是Buffer可视化的分割区域
- Tab可以组织窗口为一个工作区
Buffer
Buffer-什么是缓冲区?
- Vim 打开一个文件后会加载文件内容到缓冲区
- 之后的修改都是针对内存中的缓冲区,并不会直接保存到文件
- 直到我们执行:w (write)的时候才会把修改内容写入到文件里
Buffer切换
那如何在buffer之间切换呢?
- 使用:ls会列举当前缓冲区,然后使用:b n跳转到第n个缓冲区
- :bpre、:bnext、:bfirst、:blast
- 或者用:b buffer_name加上tab补全来跳转
Window窗口
窗口是可视化的分割区域
- 一个缓冲区可以分割成多个窗口,每个窗口也可以打开不同缓冲区
- <Ctrl+w>s 水平分割,<Ctrl+w>v 垂直分割。或者:sp和:vs
- 每个窗口可以继续被无限分 割(看你屏幕是否够大)
分割窗口示例
窗口分割效果
如何切换窗口
切换窗口的命令都是使用Ctrl+w(window)作为前缀
命令 | 用途 |
---|---|
<C-w>w | 在窗口间循环切换 |
<C-w>h | 切换到左边的窗口 |
<C-w>j | 切换到下边的窗口 |
<C-w>k | 切换到上边的窗口 |
<C-w>l | 切换到右边的窗口 |
如何重排窗口?
重排窗口可以改变窗口的大小:h window-resize查看文档
命令 | 用途 |
---|---|
<C-w>= | 使所有窗口等宽、等高 |
<C-w>_ | 最大化活动窗口的高度 |
<C-w>| | 最大化活动窗口的宽度 |
[N]<C-w>_ | 把活动窗口的高度设为[N]行 |
[N]<C-w>| | 把活动窗口的宽度设为[N]列 |
Tab(标签页)将窗口分组
Tab是可以容纳-系列窗口的容器(:h tabpage)
- Vim的Tab和其他编辑器不太一样,可以想象成Linux的虚拟桌面
- 比如一个Tab全用来编辑Python文件, 一个Tab全是HTML文件
- 相比窗口, Tab一般用的比较少, Tab太多管理起来也比较麻烦
Tab(标签页)操作
Tab使用不多,简单了解一下常用操作就好
命令 | 用途 |
---|---|
:tabe[edit] {filename} | 在新标签页中打开{filename} |
<C-w>T | 把当前窗口移到一个新标签页 |
:tabc [close] | 关闭当前标签页及其中的所有窗口 |
:tabo [only] | 只保留活动标签页,关闭所有其他标签页 |
Tab(标签页)切换操作
如何切换不同的标签页,一般建立两个就好,太多不好操作
Ex命令 | 普通模式命令 | 用途 |
---|---|---|
:tabn[next] {N} | {N}gt | 切换到编号为{N} 的标签页 |
:tabn[next] | gt | 切换到下一标签页 |
:tabp[previous] | gT | 切换到上一标签页 |
vim设置
设置文件行号:set nu,取消文件行号:set nonu
设置高亮:syntax on常用操作总结
u:undo,撤销上次命令
ctrl + r:redo,恢复撤销的命令