Tip 27 遇见Vim的命令行
当我们按下:
,Vim就切换到了底行模式.这个模式和shell的命令行有些类似.我们只要输入点命令,然后按下<CR>
就能执行.使用<Esc>
可以从底行模式切换到命令行模式.
由于一些历史原因,我们执行的命令叫做 Ex Commands
.当我们按下/
之后,或者使用<C-r>=
访问表达式寄存器的时候,也都会进入底行模式.这章中提到的一些小技巧也都适用于不同的情况,但是在这个章节,我们先讨论Ex commands
1 | 命令 效果 |
我们可以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}
注意,这个start
和end
都是地址,目前我们知道的地址是行号.在后面的我们将见到通过匹配和标记指定的地址.
.
可以用来表达为当前行,所以,我们可以通过下面的命令打印从当前行到文件末尾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
6Shopping list
Hardware Store
Buy new hammer
Beauty Parlor
Buy nail polish remover
Buy nails
通过:t命令复制行
我们的购物清单还不完整.假设我们也需要在Hardware Store
买nails
.为了修正这个清单,我们重用文件的最后一行.我们可以简单的使用:copy
命令完成
1 | Shopping list |
上述命令是通过: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
5var foo = 1
var bar = 'a'
var baz = 'z'
var foobar = foo + bar
var foobarbaz = foo + bar + baz
A;<Esc>
跳转到行结尾,输入;
,退出插入模式jVG
选中除了第一行的的后面所有行'<,'>normal.
normal执行命令模式,.
重复
1 | var foo = 1; |
使用了上面的步骤,不管是5行还是5000行,都能正常工作.
其实不仅仅是.
这个命令,使用了normal
这个标记之后,我们可以执行任何命令模式下命令.
在这个例子中,我们可以通过一个命令完成操作.1
:%normal A;
%
这个命令表示整个文件范围的行.所以上面的命令的意思是:在文件的每一行后面都追加一个;
,而且Vim会在完成之后,自动切换到命令模式
既然可以通过:normal
这个命令使用所有的命令模式命令.那么下面这个命令也很同意理解.
1 | :%normal i// |
在所有行的开始加入//
.