Vim实用技巧

基本操作#

模式#

普通模式(normal mode)#

使用 <Esc> 回到普通模式

插入模式(insert mode)#

使用i, a等命令可以从普通模式进入到插入模式

替换模式(replace mode)#

插入模式的一种特例,在替换模式中输入会替换文档中的已有文本,除此之外,该模式与插入模式完全相同.

R命令会进入该模式

插入-普通模式#

是插入模式的一个子模式,可以让我们执行一个普通模式命令,之后马上又回到插入模式.

<C-o> 会进入该模式

可视模式(visual mode)#

v<C-v>(有些插件是 <C-q>) 进入该模式

常用操作符列表#

  • d delete
  • c change
  • y yank(复制操作)
  • ~ 大小写切换
  • g~ 大小写切换
  • gu 转成小写
  • gU 转成大写
  • > 右缩进
  • < 左缩进
  • = 自动缩进
  • ! 使用外部程序过滤 {motion} 所跨越的行

使用介绍: 操作符简介,或者查看帮助: :h operator.

动作命令(motion)#

  • j 下移一行
  • k 上移一行
  • h 左移一个字符
  • l 右移一个字符
  • w 后移到下一个单词词首
  • e 后移到下一个单词词尾

上述位移操作可以前面加上数字, 表示移动多次. 比如 5k 表示上移5行.

  • ^ 移到行首(不包含空格)
  • $ 移到行尾
  • gg 回到文档第一个字符
  • G 回到文档最后一个字符
  • 0 移到行首
  • %(){} 括弧之间来回跳转

文本对象(text object)#

  • aw 光标所在的整个单词(简记 a word)
  • ap 光标所在的整个段落(简记 a paragraph)
  • it 标签内(简记 inside the tag)

修改#

  • a 在光标后进入Insert模式, i 当前光标处进入Insert模式

  • A 在行尾进入Insert模式, I 在行首进入Insert模式

  • dd 删除当前行

  • D 删除当前光标后面所有字符

  • cc 删除当前行并进入insert模式

  • C 删除当前光标后的所有字符并进入insert模式

  • x 删除当前字符

  • X 删除当前光标前一个字符

  • u 撤销操作,参考把撤销单元切成块

  • s 删除当前光标所在字符,并进入insert模式,相当于 xi

  • S 等于 cc

以退为进#

将以下代码

var foo = "method("+argument1+","+argument2+")";

+前后加上空格, 转成以下格式

var foo = "method(" + argument1 + "," + argument2 + ")";
  1. 0 移到行首
  2. f+ 查找第一个+
  3. s + <Esc> 将第一个+变成 +, 并进入normal模式
  4. ; 移动到下一个+
  5. . 重复第3步的操作
  6. ;. 继续重复操作
  7. ;. 继续重复操作

执行、重复、回退#

  • . 命令是一个微型的宏, 用于重复上次修改.
  • @: 可以用来重复任意 Ex 命令
  • & 来重复上次的 :substitute 命令(它本身也是一条 Ex 命令)
目的操作重复回退
做出一个修改{edit}.u
在行内查找下一指定字符f{char}/t{char};
在行内查找上一指定字符F{char}/T{char};
在文档中查找下一处匹配项/pattern<CR>nN
在文档中查找上一处匹配项?pattern<CR>nN
执行替换:%s/target/replacement&u
执行一系列修改qx{changes}q@xu

查找并手动替换#

:%s/content/copy/g 会将所有的content替换成copy, 可组合使用以下几个命令做到手动确认后再替换:

  1. *: 在当前文档查找当前光标所在的单词 (#*命令的反向操作)
  2. cw: 删除当前单词并进入insert模式(这个时候输入想要替换的单词)
  3. n: 查找下一个*匹配的单词
  4. .: 重复执行第二步

普通模式#

把撤销单元切成块#

在 Vim 中,u 键会触发撤销命令,它会撤销最新的修改. 我们自己可以控制撤销命令的粒度. 从进入插入模式开始,直到返回普通模式为止,在此期间输入或删除的任何内容都被当成一次修改. 因此,只要我们控制好对 <Esc> 键的使用,就可使撤销命令作用于单词、句子或段落.

在插入模式中移动光标会重置修改状态

当我提到撤销命令会回退从进入插入模式到退出此模式期间输入(或删除)的全 部字符时,我略过了一个小细节. 如果在插入模式中使用了 <Up><Down><Left><Right> 这些光标键,将会产生一个新的撤销块. 你可以把这想象为先切换回普通模式,然后用 hjkl 命令对光标进行了移动,唯一区别是我们并没有退 出插入模式. 这也会对 . 命令的操作产生影响.

构造可重复的修改#

假设光标位于行尾处的字符“h”上,而我们想要删除单词“nigh”:

The end is nigh

方式1: 反向删除

  1. db 反向删除字母nig
  2. x 删除剩下的字母h

方式2: 正向删除

  1. b 先将光标移到单词开头
  2. dw 删除整个单词

方式3: 删除整个单词

我们可以使用更为精准的 aw 文本对象(text object), 而不是动作命令:

  1. daw 直接删除光标所在的整个单词(可以简记为 delete a word)

哪种方式最具重复性?

上述的三种删除单词的方式: dbxbdw 以及 daw ,只有daw最具有可重复性(利用.命令重复删除单词):

  • dbx 是两个命令: dbx,所以最后.命令相当于x
  • bdw 由一个位移操作b和一个修改命令dw组合而成,所以最后.命令相当于dw

用次数做简单的算术运算#

<C-a><C-x> 命令分别对光标所在的整个数字执行加和减操作.

如果执行时光标不在数字上,命令会把当前光标移到之上或之后的数值,再进行加减.

和很多其他普通模式命令一样,<C-a><C-x>可以带一个次数前缀,这样 Vim 就会尝试把该命令执行指定的次数,而不是只执行一次,比如要把 .blog { background-position: 0px 0px } 变成 .blog { background-position: -180px 0px },只需要执行180<C-x>即可.

数字的格式

Vim 默认把以 0 开头的数字解释为八进制值,而不是十进制,因此对文本007执行<C-a>,会变成010. 如果想禁用Vim默认行为, 可以在vimrc里增加配置set nrformats=,这回让Vim把所有数字当成十进制处理.

操作符简介#

Vim 的强大很大程度上源自操作符与动作命令相结合:

操作符 + 动作命令 = 操作 简写 {operator}{motion}

操作符的作用范围由动作命令决定,比如:

  • dl 删除一个字符
  • daw 删除一个完整单词
  • dap 删除一整个段落

c{motion}y{motion} 也类似,它们被统称为操作符.

其他操作符 g~gugU 命令要用两次按键来调用,我们可以把上述命令中的 g 当做一个前缀字符,用以改变其后面的按键行为(这种行为被称为 结识操作符待决模式 ). daw 是删除当前单词,同理gUaw就是把当前单词转成大写形式.

Vim 的语法有一条额外规则,即当一个操作符命令被连续调用两次时,它会作用于当前行:

  • dd 删除当前行
  • >> 缩进当前行
  • gU 命令是一种特殊情况,可以用 gUgUgUU 来使它作用于当前行(把当前行全部转成大写)
  • 其他操作符使用类似
  • cc 删除当前行并进入插入模式
  • yy 复制当前行
自定义操作符与已有动作命令协同工作

随同 Vim 发布的标准操作符集合相对比较少,但我们可以定义新的操作符. Tim Pope 的 commentary.vim 插件提供了一个很好的例子, 此插件为 Vim 所支持的编程语言增添了注释及取消注释的命令.

如果你对如何创建自定义操作符感到好奇,可以先阅读一下文档 :h :map-operator.

自定义动作命令与已有操作符协同工作

Vim 缺省的动作命令集已经相当全面了,但是我们还是可以定义新的动作命令及文本对象来进一步增强它.

Kana Natsuno 的 textobj-entire 插件是一个很好的例子,它为 Vim 增加了两种新的文本对象 ieae ,它们作用于整个文件.

如果想用 = 命令自动缩进整个文件,我们可以执行 gg=G (就是说,先用 gg 跳到文件开头,然后用 =G 自动缩进从光标位置到文件结尾的所有内容). 但是如果我们安装了 textobj-entire 插件的话,简单地执行 =ae 就可以了. 运行这条命令时光标在哪儿并不重要,因为它总是作用于整个文件.

如果你对如何创建自定义动作命令感到好奇,可以由阅读 :h omap-info

结识操作符待决模式#

普通、插入及可视模式很容易辨识,但是 Vim 还有另外一些很容易被忽视的模式,操作符待决模式(Operator-Pending mode)就是一个例子. 每天我们无数次地使用它,但通常它只持续不到一秒时间. 举个例子,在我们执行命令 dw 时,就会激活该模式. 这一模式只在按 dw 键之间的短暂时间间隔内存在,一眨眼工夫就不见了.

如果我们把 Vim 想象成有限状态机,那么操作符待决模式就是一个只接受动作命令的状态. 这个状态在我们调用操作符时被激活,然后什么也不做,直到我们提供了一个动作命令,完成整个操作. 当操作符待决模式被激活时,我们可以像平常一样按 <Esc> 中止该操作,返回到普通模式.

很多命令都由两个或更多的按键来调用(查阅 :h g:h z:h ctrl-w ,或者 :h [ , 可以看到一些例子) ,但在多数情况下,头一个按键只是第二个按键的前缀. 这些命令不会激活操作符待决模式,相反,可以把它们当成命名空间(namespace), 用来扩充可用命令的数目. 只有操作符才会激活操作符待决模式.

你也许想知道,为什么要有一个完整的模式,专门用于操作符和动作命令之间的短暂瞬间,而命名空间命令则仅仅是普通模式的一个扩充?好问题!这是因为我们能够创建自定义映射项来激活或终结操作符待决模式. 换句话说,它允许我们创建自定义的操作符及动作命令,从而让我们可以扩充 Vim 的词汇.

插入模式#

在插入模式中可即时更正错误#

以下按键操作在插入模式下使用:

按键操作用途
<C-h>删除前一个字符(同退格键)
<C-w>删除前一个单词
<C-u>删至行首

返回普通模式#

按键操作用途
<Esc>切换到普通模式
<C-[>切换到普通模式
<C-o>切换到插入-普通模式

结识插入-普通模式#

插入-普通模式是普通模式的一个特例,在此模式中,我们可以执行一个普通模式命令,执行完后,马上就又返回到插入模式. 要从插入模式切换到插入-普通模式,可以按 <C-o> (参见 :h i_CTRL-O ).

info

zz命令可以重绘屏幕,并把当前行显示在窗口正中,这样就能够阅读当前行之上及之下的半屏内容. 我常常会键入 <C-o>zz ,在插入-普通模式中触发这条命令. 此操作完成后就会直接回到插入模式,因此我可以不受中断地继续打字.

插入模式粘贴寄存器中的文本#

插入模式, <C-r>{register} 会把复制的文本粘贴到光标所在位置, 其中 {register} 是我们想要插入的寄存器的名字(参见 :h i_CTRL-R ).

假设光标位于行首,针对文本 hello world. 执行以下操作:

  1. yawhello 复制到复制专用寄存器中
  2. A 行尾进入插入模式
  3. <C-r>0 行尾粘贴 hello 字符
  4. 操作完成之后仍是插入模式,文本内容变成 hello world.hello

对面向字符的寄存器使用 <C-r>{register} 命令#

Vim插入寄存器内的文本时,其插入方式就如同这些文本是由键盘上一个个输进来的. 因此,如果 textwidth 或者 autoindent 选项被激活了的话,可能会出现不必要的换行或额外的缩进,粘贴大量文本会有轻微延时.

<C-r><C-p>{register} 命令则会更智能一些,它会按原义插入寄存器内的文本,并修正任何不必要的缩进(参见 :h i_CTRL-R_CTRL-P ),不过这个命令有点不太好输入!因此,如果我想从一个寄存器里粘贴很多行文本的话,我更喜欢切换到普通模式,然后使用某个粘贴命令.

表达式寄存器做运算#

插入模式执行<C-r>=,然后输入表达式按下回车键<CR>,Vim就会把表达式执行结果插入到光标所在位置.

比如对文本 6 chairs, each costing $35, totals $ 执行以下操作:

  1. A 在行尾进入插入模式
  2. <C-r>=6*35<CR> 执行表达式
  3. 操作完成后仍是插入模式,文本内容变成 6 chairs, each costing $35, totals $210

用字符编码插入非常用字符#

插入模式执行<C-v>{code}可以插入任意字符,比如:

  • <C-v>065 会插入 A (八进制)
  • <C-v>00bf 会插入 ¿ (hex十六进制)
  • 普通模式下, ga 可以在状态栏显示当前光标下的字符的编码

用二合字母插入非常用字符#

插入模式执行<C-k>{char1}{char2}可以插入以二合字母{char1}{char2}表示的字符, 比如:

  • <C-k>?I => ¿
  • <C-k>12 => ½
  • <C-k>14 => ¼

用命令 :digraphs 或者 :h digraph-table 可以查看可用的二合字母列表

用替换模式替换已有文本#

R 会进入替换模式,输入字符即可完成替换.

gR 会进入虚拟替换模式. 该模式会把制表符当成一组空格处理,而R会把制表符当成一个字符处理.

r{char}gr{char} 是单次替换,替换完成后又回到普通模式

可视模式#

某些可视模式命令执行的基本功能与普通模式相同,但操作上有些细微的变化. 例如,在这两种模式中, c 命令的功能是一样的,都是删除指定的文本并切换到插入模式. 不过,要指定其所操作的范围,二者的方式却不甚相同. 在普通模式中,我们先触发修改命令,然后使用动作命令指定其作用范围. 然而在可视模式中,我们先选中选区,然后再触发修改命令. 这种次序颠倒的方式对所有的操作符命令都适用.

结识选择模式#

在一个典型的文本编辑器环境中,当选中一段文本后,再输入任意可见字符时,这些选中的文本将会被删除. 虽然 Vim 的可视模式未遵从此惯例,但是其选择模式( Select Mode )却按此方式工作.

在进入可视模式后,按 <C-g> 可以在可视模式及选择模式间切换. 切换后看到的唯一不同是屏幕下方的提示信息会在 “-- 可视 --” ( -- VISUAL -- )及“--选择--” ( --SELECT-- )间转换. 如果在选择模式中输入任意可见字符的话,此字符会替换所选内容并切换到插入模式.

按键操作用途
v激活/退出 面向字符的可视模式
V激活/退出 面向行的可视模式
<C-v>激活/退出 面向列块的可视模式
gv重选上次的高亮选区
o切换高亮选区的活动端

切换选区的活动端#

如果选取定义到一半发现开始位置不对,可以按下o,将光标移动到选区的开端.

列块可视模式下批量插入文本#

  1. <C-v>进入可视模式
  2. 利用动作命令创建选好范围(比如$选到行尾,即便行的长度不一样)
  3. 使用AI进入插入模式然后输入文本(也可直接使用r执行替换, ~大小写转换)
  4. 然后按下<Esc>进入到普通模式

示例, 对以下文本的行尾添加;:

var foo = 1
var bar = 'a'
var foobar = foo + bar

执行<C-v>jj$A;<Esc>即可.

Vim对i及a键的约定

Vim 对于从普通模式切换到插入模式的命令有几个约定, i 命令和 a 命令都完成此切换,并分别把光标置于当前字符之前或之后, I 命令和 A 命令的表现类似,只是它们分别把光标置于当前行的开头和结尾.

Vim 对于从列块可视模式切换到插入模式的命令也遵从类似的约定. I 命令和 A 命令都完成此切换,并分别把光标置于选区的开头和结尾. 那 ia 命令呢,它们在可视模式里干什么?

在可视模式及操作符待决模式中, ia 键沿用一个不同的约定. 它们会被当作一个文本对象的组成部分,我们将在技巧 51 中深入探讨文本对象. 如果你在列块可视模式里选中了一块区域,并且很奇怪为什么按 i 键没进入插入模式,那么换用 I 键试一下.

命令行模式#

Vim 的先祖是 vi,正是 vi 开创了区分模式编辑的范例. 相应的,vi 奉一个名为 ex 的行编辑器为先祖,这就是为什么会有 Ex 命令.

在按下 : 键时,Vim 会切换到命令行模式. 输入一条命令,然后按 <CR> 执行它. 在任意时刻,我们都可以按 <Esc> 键从命令行模式切换回普通模式.

在我们按 · 调出查找提示符或用 <C-r>= 访问表达式寄存器时,命令行模式也会被激活. 本节介绍的一些技巧在这些不同的提示符下都适用.

几乎所有功能都有相应的 Ex 命令(参见 :h ex-cmd-index),Ex 命令影响范围广且距离远,因此普通模式命令适合在本地进行操作,而 Ex 命令则可以远距离操作.

结识Vim的命令行模式#

  • 读写文件 :edit:write
  • 创建新标签页 :tabnew
  • 分割窗口 :split
  • 操作参数列表 :prev / :next
  • 缓冲区列表 :bprev / :bnext
  • 重复上次的 Ex 命令 @:

操作缓冲区文本的Ex命令#

命令用途
:[range]delete [x]删除指定范围内的行[到寄存器 x 中]
:[range]yank [x]复制指定范围的行[到寄存器 x 中]
:[line]put [x]在指定行后粘贴寄存器 x 中的内容
:[range]copy {address}把指定范围内的行拷贝到 {address} 所指定的行之下
:[range]move {address}把指定范围内的行移动到 {address} 所指定的行之下
:[range]join连接指定范围内的行
:[range]normal {commands}对指定范围内的每一行执行普通模式命令 {commands}
:[range]substitute/{pattern}/{string}/[flags]把指定范围内出现{pattern}的地方替换为{string}
:[range]global/{pattern}/[cmd]对指定范围内匹配{pattern}的所有行,在其上执行 Ex 命令{cmd}

Vim命令行模式中的特殊按键#

有些命令在插入模式和命令行模式中可以通用. 例如:

  • 可以用 <C-w><C-u> 分别删除至上个单词的开头及行首
  • 可以用 <C-v><C-k> 来插入键盘上找不到的字符
  • 可以用 <C-r>{register} 命令把任意寄存器的内容插入到命令行

在一行或多个连续行上执行命令#

  • :[range]print 回显指定范围的内容,它不产生什么实际影响
  • :[range]delete 删除指定范围的内容
  • :[range]join 指定范围的内容连接起来

如果不指定范围(没有[range]),默认是当前行.

用行号作为地址#

如果输入一条只包含数字的 Ex 命令,那么 Vim 会把这个数字解析成一个地址,并把光标移动到该数字所指定的行上:

  • :1 光标移到第一行
  • :$ 光标移到最后一行
  • :5p 光标移到第5行,并回显当前行内容(pprint的简写)

用地址指定一个范围#

:{start},{end} 可以用来指定一个范围:

  • :2,5p 回显第2到5行内容
  • :.,$p 回显当前行到文件末尾所有行, . 表示当前行
  • :%p 回显当前文档所有内容, %表示当前稳当的所有行,等价于 :1,$p
  • :%s/Hello/World/ 对当前文档所有行进行替换,把 Hello 替换成 World
  • :78,86s/,/;/ 把78到86行中的逗号换成分号

用高亮选区指定范围#

使用选区命令创建好选区之后,按下:就会进入Ex模式,并且会预先填充一个范围 :'<,'>,接下来我们就可以输入一条 Ex 命令,使它在每个被选中的行上执行:

  • :'<,'>p 回显选区内容
  • :'<,'>s/'/"/g 把选区内的单引号变成双引号

用模式指定范围#

Vim 也接受以模式作为一条 Ex 命令的地址,比如 :/<html>/,/<\/html>/p,实际上也符合范围的一般形式 :{start},{end}{start} 地址是模式 /<html>/ ,而 {end} 地址是 /<\/html>/ . 换句话说, 这个范围由 <html> 开标签所在的行开始,到对应闭标签所在的行结束.

用偏移对地址进行修正#

偏移的一般形式是 :{address}+n,如果 n 被省略,那么缺省偏移量为 1. {address} 可以是一个行号、一个位置标记,或是一个查找模式.

  • :/<html>/+1,/<\/html>/-1p 回显 <html></html> 之间的内容,但是不包括 <html></html> 标签所在的行
  • :.,.+3p 回显当前行(.)及当前行下三行(.+3)的内容,如果当前行是第2行,就相当于 :2,5p

Ex命令地址及范围符号总结#

符号地址
1文件的第一行
$文件的最后一行
0虚拟行,位于文件第一行上方
.当前行
'm包含位置标记 m 的行
'<高亮选区的起始行
'>高亮选区的结束行
%整个文件(:1,$ 的简写形式)

第 0 行在文件中并不真实存在,但它作为一个地址,在某些特定场景下会很有用处. 特别是,在把指定范围内的行复制或移动到文件开头时,可以用它做 :copy {address}:move {address} 命令的最后一个参数.

使用:t和:m命令复制和移动行#

:[range]copy {address} 命令(copy可以简写成co或者t)可以把一行或多行从文档的一部分复制到另一部分

:[range]move {address} 命令则可以让我们把一行或多行移到文档的其他地方(move可以简写成m)

比如(可以把下面例子中的t换成m即可实现移动):

  • :.t. 等同于:t., 复制并粘贴当前行,相当于执行 Ypyyp 命令(不同点是yy或者Y会腐败默认寄存器中的内容,而:t.不会)
  • :5t. 把第5行复制到当前行下面
  • :3,5t$ 把第3到5行复制到文档底部
  • :.+3t0 把当前行上方的第三行复制到文档顶部
  • : 把当前行上方的第三行复制到文档顶部
  • :6t. 把第 6 行复制到当前行下方
  • :t6 把当前行复制到第 6 行下方
  • :t$ 把当前行复制到文本结尾
  • :'<,'>t0 把选中的行复制到文件开头

在指定范围上执行普通模式命令#

如果想在一系列连续行上执行一条普通模式命令,我们可以用 :normal 命令:

  • :3,5normal gUU 第3到5行转成大写
  • :.,.+3normal i// 将当前行及后面三行的行首添加 //
  • :'<,'>normal . 对高亮选区中的每一行, 对其执行普通模式下的 . 命令(即对选区内文本重复执行上次执行的普通模式命令)
  • %normal A; 文件每行的结尾都添加一个分号(修改完后 Vim 会自动返回到普通模式. )

重复上次的Ex命令#

  • . 可以重复上次的普通模式命令,而 @: 可以重复上次的Ex命令.
  • <C-o> 命令会回到跳转列表的上条记录

自动补全Ex命令#

  • <Tab> 自动补全命令
  • <S-Tab> 反向遍历补全列表
  • <C-d> 显示可用的补全列表

在多个补全项间选择#

调整 wildmode 选项可以自定义补全行为(参见 :h 'wildmode' 习惯用 bash shell 的方式工作,那么下面的设置会满足你的需要:

.vimrc
set wildmode=longest,list

如果你习惯于 zsh 提供的自动补全菜单,或许会想试试这个:

.vimrc
set wildmenu
set wildmode=full

wildmenu 选项被启用时,Vim 会提供一个补全导航列表. 我们可以按 <Tab><C-n><Right> 正向遍历其列表项, 也可以用 <S-Tab><C-p><Left>

把当前单词插入到命令行#

在 Vim 的命令行下, <C-r><C-w> 映射项会复制光标下的单词并把它插入到命令行中:

  • /\<<C-r><C-w>\><CR> 等效于 * 命令,即查找当前光标下的单词
  • :%s/hello/<C-r><C-w>/ghello替换成当前光标下的单词
  • 把光标移到中文句号, 然后执行 :%s/<C-r><C-w>/. /g 即可把中文句号替换成 .

其他快捷键(:h c_CTRL-R_CTRL-W):

  • <C-r><C-a> 插入当前光标下的字符串

回溯历史命令#

在进入Ex命令行模式(:)、查找模式(/)后可以按以下按键填充之前执行过的命令:

  • <Up>、向上箭头、<C-p> 反向遍历历史命令
  • <Down>、向下箭头、<C-n> 正向遍历历史命令

也可以输入 :help 然后在遍历历史命令, 这样只展示包含help的命令.

模式Vim会记录最后20条命令,可以修改vimrc配置,提高上限:

.vimrc
set histroy=200

结识命令行窗口#

可以将两个命令使用 | 连接合成一条使用, 比如:

:write # 保存文件内容
:!node % # 使用node执行当前文件

合并后变成:

:write | !node %

在普通模式下使用q:命令可以打开命令行窗口(参见 :h cmdwin), 命令行窗口中的每行内容都对应着命令历史中的一个条目. 在命令行窗口中可以使用Vim全部功能来查找编辑修改命令, 然后使用回车键<CR>时, 将会把当前行的内容当成 Ex 命令加以执行.

note

当命令行窗口处于打开状态时,它会始终拥有焦点. 这意味着,除非关闭命令行窗口,否则我们无法切换到其他窗口. 要想关闭命令行窗口,我们可以执行 :q 命令(就像关闭普通 Vim 窗口那样),或是按 <CR> .

当我们在命令行窗口内按 <CR> 时,该命令在活动窗口的上下文中执行. 活动窗口指的是在调出命令窗口前,处于活动状态的那个窗口. 当命令行窗口处于打开状态时,Vim 并不会提示哪个窗口是活动窗口,因此如果你用了分割窗口的话,需要特别留意.

运行Shell命令#

不用离开Vim, 我们还可以把缓冲区的内容作为标准输入发送给一个外部命令,或是把外部命令的标准输出导入到缓冲区里.

执行Shell中的程序#

在命令行模式中, 给命令加一个叹号前缀(参见 :h :! )就可以调用外部程序 :!{cmd}:

  • :!ls -al 查看当前目录下的所有文件

在命令行中,符号 % 代表当前文件名(参见 :h cmdline-special ). 在运行那些操作当前文件的外部命令时,我们可以使用它:

  • :!ruby % 使用Ruby执行当前文件

:!{cmd} 适用于执行一次性命令, 如果想在 shell 中执行多条命令, :shell 命令来启动一个交互的 shell 会话(参见 :h :shell ), 执行完shell后使用exit退出shell回话并返回Vim.

把Vim置于后台

:shell 命令是 Vim 提供的一个功能,它可以让我们切换到一个交互 shell 中。但是,如果 Vim 自身是在终端中运行的话, 可以利用 bash shell 的作业控制, 暂停一个作业,把它放到后台,然后在稍后某个时间再把它调回前台继续运行.

假设我们正在 bash shell 中运行 Vim,然后需要执行一些 shell 命令。我们可以先按 Ctrl-z 挂起 Vim 所属的进程,并把控制权交还给 bash。此时 Vim 进程在后台处于挂起状态,让我们可以像往常一样与 bash 会话进行交互。

在 bash 中,我们可以用 fg 命令唤醒一个被挂起的作业,把它移到前台。这会让 Vim 恢复成挂起前的状态。 Ctrl-zfg 命令比 Vim 所提供的 :shellexit 命令更加方便快捷。

把缓冲区内容作为标准输入或输出#

  • :read !{cmd} 命令把 {cmd} 命令的输出读入当前缓冲区中.
  • :write !{cmd} 命令把缓冲区内容作为指定 {cmd} 的标准输入.

下面展示一个重命名文件的例子(例子来自:h rename-files), 把当前目录下的.js扩展名改成.ts:

  1. 打开bash shell, 执行vim进入Vim环境
  2. :r !ls *.js 会把当前目录下的所有.js文件读到当前缓冲区, 其中:r:read的简写
  3. :%s/\(.*\).js/mv & \1.ts 把每行文本替换成shell脚本
  4. :w !sh 把缓冲区内容作为标准输入传递给 sh 执行.
  5. :q! 退出Vim

使用外部命令过滤缓冲区内容#

[range]!{cmd} 当给定一个范围时, [range] 所指定的行会传给 {cmd} 作为标准输入,然后又会用 {cmd} 的输出覆盖 [range] 内原本的内容, 也就是说 [range] 内的文本会被指定的 {cmd} 进行过滤. Vim 把过滤器定义为“一个由标准输入读取文本,并对其进行某种形式的修改后输出到标准输出的程序”.

利用把以下文本中的姓氏按照第二个字段重排:

文本内容
first name,last name,email
john,smith,john@example.com
drew,neil,drew@vimcasts.org
jane,doe,jane@example.com

执行命令:2,$!sort -t',' -k2替换即可.

Tricks#

普通模式#

按键操作用途备注
zz把当前行显示在窗口正中
zt把当前行显示在窗口顶部top
zb把当前行显示在窗口底部bottom
K查看处于光标之下的那个单词的手册页
J把当前行和下一行连接在一起
ga在状态栏显示当前光标下的字符的编码
Vr-当前行字符全部替换成-
<C-o>跳回上一个位置
*查找当前单词

Percent#

  • 在普通模式中 %用来在(){}之间来回跳转,可以搭配操作符操作.
  • 在Ex命令模式中,%表示整个文档.

大小写切换#

~ 对当前字符进行大小写切换, 无需结合动作命令(motion).

g~(大小写切换)、gu(变成小写) 和 gU(变成大写) 需要结合动作命令:

  • gUaw 当前单词转成大写
  • gUit 当前标签内文本变成大写

或者调用两次时作用于当前行:

  • gUgU 或者 gUU 当前行全部变成大写

(g~gu使用方式一样)

文本对象(text object)#

按键操作用途备注
caw删除当前单词,并进入插入模式aw 可以记成 a word
ciw删除当前单词,并进入插入模式
cit删除标签内单词,并进入插入模式it 可以记成 inside the tag

上面的c可以替换成其他操作符dy,甚至 v(可视模式).

缩进#

>用于缩进, > 可以配合其他位移操作使用

  • >> 缩进当前行
  • >G 缩进当前行及后所有行

字符查找#

  • f{char} 在行内查找下一个定字符(光标会移动到字符)
  • F{char} 在行内查找上一个定字符(光标会移动到字符)
  • t{char} 在行内查找下一个定字符(光标停留在字符前)
  • T{char} 在行内查找上一个定字符(光标停留在字符前)
  • ; 命令会重复查找上次 f 命令所查找的字符, 是移动可重复执行
  • ;相反,回到上一个查找的字符
  • * 查找当前光标下的单词