Vim实践Tips(六)

Tip 27 遇见Vim的命令行

当我们按下:,Vim就切换到了底行模式.这个模式和shell的命令行有些类似.我们只要输入点命令,然后按下<CR>就能执行.使用<Esc>可以从底行模式切换到命令行模式.

由于一些历史原因,我们执行的命令叫做 Ex Commands .当我们按下/之后,或者使用<C-r>=访问表达式寄存器的时候,也都会进入底行模式.这章中提到的一些小技巧也都适用于不同的情况,但是在这个章节,我们先讨论Ex commands

1
2
3
4
5
6
7
8
9
10
11
12
  命令                     效果
:[range]delete[x] 删除特定的行(放到寄存器x中)
:[range]yank[x] 复制特定的行(放到寄存器x中)
:[line]put[x] 把寄存器x中的内容放到特定的行后面
:[range]copy{address} 复制特定的行到通过地址指定的行的下面
:[range]move{address} 移动特定的行到通过地址指定的行的下面
:[range]join 连接指定的行
:[range]normal {commands} 对特定的范围执行命令模式下的命令
:[range]substitute/{pattern}
/{string}/{flags}
在特定的行里面,把符合条件的匹配使用字符串替换掉.
:[range]global/{pattern}/[cmd] 在所有匹配到pattern的行中,执行Ex命令

我们可以Ex命令读写文件(:read:write),或者使用:tabnew命令创建Tab页,或者使用:split命令创建窗口.

Tip 28 在一到多个连续行中执行命令

许多Ex命令都可以接受一个{range},我们可以通过行号,标记,或者是一个Pattern来提供范围.
假设有下面一段代码:

1
2
3
4
<html>
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>
</html>

为了说明,我们使用:print命令,这个命令可以简单的通过这个命令将特定的行打印到Vim的底行之下.
这个命令没有什么特定的功能.但是可以用来做说明.当然,你也可以通过:delete,:join,:substitude,:normal这几个命令来测试.通过测试,你就可以知道怎么使用Ex命令了.

使用行号作为地址

如果我们输入一个仅由数字组成的Ex命令.Vim就会把这个命令当做地址,然后将光标移动到那一行.所以,我们就能通过下面的这个命令跳转到文件顶部:

1
2
:1
:print

你也可以使用:p来打印,这个是:print的缩写形式.你也可以将两个命令合并到一起.

1
:3p

这个命令移动到第3行,然后打印该行的内容.我们只是通过:p命令来说明问题,下面你可以试试这个命令.

1
:3d

这个命令跳转到第3行,然后执行删除命令.它相当于在命令模式下执行:3Gdd.这个命令比命令行模式下的要快点.

通过地址指定一个范围

1
:2,5p

这个命令可以打印从第2行到第5行,并且最后光标停留在第5行.通常来说,范围可以表现为这个形式:

1
:{start},{end}

注意,这个startend都是地址,目前我们知道的地址是行号.在后面的我们将见到通过匹配和标记指定的地址.

.可以用来表达为当前行,所以,我们可以通过下面的命令打印从当前行到文件末尾

1
2
:2
:.,$p

%这个符号也有特别的意义,它代表了当前文件中的所有行.

1
:%p

它相当于:1,$p这个命令.

通过在可视化模式下的选择指定范围

首先通过命令2G跳转到第2行,然后VG可以选择从2行到文件结尾.此时,我们按下:.这时候,底行上出现了:'<,'>,看起来有点怪怪的,但是你可以简单的认为,它就代表了可视化模式的选区.然后,我们就能指定Ex命令.例如:

1
:'<,'>p

通过匹配指定范围

1
:/<html>/,/<\/html>/p

打印结果

1
2
3
4
<html>
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>
</html>

看起来有点复杂,但是,它还是遵循了:{start},{end}的形式.这个start和刚刚的/<html>/相对应.而end/<\/html>/相对应.

在这个例子中,我们可以通过:2,5指定范围,这个方式更简洁.而通过标签形式的方式,可以直接匹配标签,不论中间有多少行.

通过偏移来指定地址

假设我们有需求:打印所有的<html>标签内的内容,但是不打印包含<html>的行.
我们可以指定偏移:

1
:/<html>/+1,/<\/html>/-1p

打印结果

1
2
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>

这个语法的一般形式:

1
{address}+n

如果n省略,那么它默认是1.这个address可以是行号或者是标记或者是模式匹配
那么我们可以实现,从当前行打印后面3行

1
:.,.+3p

讨论

指定范围的语法非常灵活.我们可以混合使用行号 标记 模式匹配,也可以通过应用偏移来修改范围.
下面的表格可以作为一个参考:

1
2
3
4
5
6
7
8
9
符号      代表的地址
1 文件的第一行
$ 文件的最后一行
. 光标所在行
'm 标记m所在的行
'< 可视化选区的开始
'> 可视化选区的结尾
% 文件所有行 ( :1,$ 的快捷方式)
0 第0行

第0行并不真实存在,但是在特定的情况下,这个地址还是很有用的.例如,在:copy {address}:move {address}中,我们想复制或者移动从文件开头的范围.在后面的两个Tip中,我们将看到具体的例子.

当我们指定一个[range],它总是表示一些连续的行.我们也能通过模式匹配应用Ex命令到一系列非连续的行,通过:global这个命令就能做到.

Tip 29 通过t和m复制和移动行

:copy命令(快捷方式是:t)让我们可以复制1到多行,而:move命令可以移动1到多行.

为了说明,我们使用下面的代码:

1
2
3
4
5
6
Shopping list
Hardware Store
Buy new hammer
Beauty Parlor
Buy nail polish remover
Buy nails

通过:t命令复制行

我们的购物清单还不完整.假设我们也需要在Hardware Storenails.为了修正这个清单,我们重用文件的最后一行.我们可以简单的使用:copy命令完成

1
2
3
4
5
6
7
Shopping list
Hardware Store
Buy nails
Buy new hammer
Beauty Parlor
Buy nail polish remover
Buy nails

上述命令是通过:160copy.完成的(在我编写这个文档的时候,Buy nails 是第160行`)

copy命令的一般格式是:

1
:[range]copy {address}

在我们的例子中,[range]160行.{address}在这个例子中是.,代表了当前行.所以这个:160copy.的意义是:复制160行,并且放置到当前行的下面.

我们也可以简写:copy:co,也可以更简洁的写成:t.为了辅助记忆,你可以理解t为:copy To.下面的表格中列举了一些:t的用法:

1
2
3
4
5
6
命令             效果
:6t. 复制第6行到当前行的下面
:t6 复制当前行到第6行下面
:t. 复制当前行(相当于命令模式下的yyp)
:t$ 复制当前行到文件结尾
:'<,'>t0 复制可视化选区到文件开头

注意: :t.复制当前行.作为选择,我们也可以通过命令模式下的yyp实现同样的效果.一个值得注意的区别就是yyp使用寄存器,而:t.不这样.有时候,为了避免覆盖默认寄存器的内容,我使用:t.复制当前行.

在这个例子中,我们使用yyp的变体复制我们想要的行,但是,它需要一些额外的移动.我们需要先跳转到到我们想复制的行6G,复制yy,回到我们开始的地方<C-o>,然后p粘贴.所以,在这种比较远的复制操作,:t这种命令更高效.

通过:m命令移动行

语法是:

1
:[range]move {address}

我们可以简写成:m
如果已经选择好了分区.我们可以直接运行命令:'<,'>m$,作为选择,我们也可以使用dGp命令.这个命令可以分解为:d删除,同时复制到寄存器,G跳转到文件结尾,p粘贴内容.

重复上个Ex命令: @:

Tip 30 在一个范围上使用命令模式的命令

在Tip 2 中,我们在每行的后面追加一个分号,当时我们使用的.命令做的重复.当时只有几行,那么做可以,但是如果有2000行需要追加呢? 显然,用j.的方式就不靠谱了.

使用normal命令可以对一个范围使用命令模式下的命令.

我们使用下面的代码来说明问题:

1
2
3
4
5
var foo = 1
var bar = 'a'
var baz = 'z'
var foobar = foo + bar
var foobarbaz = foo + bar + baz

  1. A;<Esc> 跳转到行结尾,输入;,退出插入模式
  2. jVG 选中除了第一行的的后面所有行
  3. '<,'>normal. normal执行命令模式,.重复
1
2
3
4
5
var foo = 1;
var bar = 'a';
var baz = 'z';
var foobar = foo + bar;
var foobarbaz = foo + bar + baz;

使用了上面的步骤,不管是5行还是5000行,都能正常工作.
其实不仅仅是.这个命令,使用了normal这个标记之后,我们可以执行任何命令模式下命令.

在这个例子中,我们可以通过一个命令完成操作.

1
:%normal A;

%这个命令表示整个文件范围的行.所以上面的命令的意思是:在文件的每一行后面都追加一个;,而且Vim会在完成之后,自动切换到命令模式

既然可以通过:normal这个命令使用所有的命令模式命令.那么下面这个命令也很同意理解.

1
:%normal i//

在所有行的开始加入//.