vi 常用命令

发表信息: by Creative Commons Licence

  • 文章标签:
  • vi 1

为什么我们应该学习 vi

在现在这个图形编辑器和易于使用的基于文本编辑器的时代,比如说 nano,为什么我们还 应该学习 vi 呢?下面有三个充分的理由:

  • vi 很多系统都预装。

    如果我们的系统没有图形界面,比方说一台远端服务器或者是一个 X 配置损坏了的本地系统,那么 vi 就成了我们的救星。

    虽然 nano 逐渐流行起来,但是它还没有普及。

    POSIX,这套 Unix 系统中程序兼容的标准,就要求系统要预装 vi。

  • vi 是轻量级且执行快速的编辑器。

    对于许多任务来说,启动 vi 比起在菜单中找到一个图形化文本编辑器,再等待编辑器数倍兆字节的数据加载而言,要容易的多。

    另外,vi 是为了加快输入速度而设计的。

    我们将会看到,当一名熟练的 vi 用户在编辑文件时,他或她的手从不需要移开键盘。

  • 我们不希望其他 Linux 和 Unix 用户把我们看作胆小鬼

好吧,可能只有两个充分的理由。

背景介绍

第一版 vi 是在 1976 由 Bill Joy 写成的,当时他是加州大学伯克利分校的学生,后来他共同创建了 Sun 微系统公司。

vi 这个名字来源于单词 "visual",因为它打算在带有可移动光标的视频终端上编辑文本。

在发明可视化编辑器之前,有一次只能操作一行文本的行编辑器。为了指定一个修改,我们告诉行编辑器到一个特殊行并且说明做什么修改,比方说添加或删除文本。

视频终端 (而不是基于打印机的终端,像电传打印机) 的出现,可视化编辑成为可能。

vi 实际上整合了一个强大的叫做 ex 行编辑器,所以我们在使用 vi 时能运行行编辑命令。

启动和停止 vi

启动 vi: vi

停止 vi: :q

强制退出: q!

注意:冒号是命令的一部分

小贴士:如果你在 vi 中 "迷失" 了,试着按下 Esc 键两次来找到路(回到普通模式)。

创建新文件

启动 vi 时, 传递给 vi 一个不存在的文件名,就创建了一个新文件:

vi foo.txt

每行开头的波浪号 ("~") 指示那一行不存在文本,这表示我们有一个空文件,还没有输入 任何字符。

小贴士:vi 是一个模式编辑器。

当 vi 启动后,进入的是命令模式;这种模式下,几乎每个按键都是一个命令,所以如果 我们打算输入字符,vi 会发疯,弄得一团糟。

插入模式

为了在文件中添加文本,首先我们必须进入插入模式。

按下 "i" 按键进入插入模式,现在我们能输入一些文本了,试着输入这些文本:

The quick brown fox jumped over the lazy dog.

按下 Esc 按键, 退出插入模式并返回命令模式。

保存我们的工作

为了保存我们刚才对文件所做的修改,我们必须在命令模式下输入一个 ex 命令,通过按下 ":" 键,这很容易完成。

按下冒号键之后,一个冒号字符应该出现在屏幕的底部: :

为了写入我们修改的文件,我们在冒号之后输入 "w" 字符,然后按下回车键: :w

文件将会写入到硬盘,并且我们应该在屏幕底部得到一个确认信息,就像这样:

"foo.txt" [New] 1L, 46C written

小贴示:如果你阅读 vim 的文档,你注意到 (混淆地) 命令模式被叫做普通模式,ex 命令叫做命令模式,当心。

移动光标

当在 vi 命令模式下时,vi 提供了大量的移动命令,其中一些是与 less 阅读器共享的。这里列举了一些:

按键 移动光标
l 或 右箭头 向右移动一个字符
h 或 左箭头 向左移动一个字符
j 或 下箭头 向下移动一行
k 或 上箭头 向上移动一行
0 (零按键) 移动到当前行的行首
ˆ 移动到当前行的第一个非空字符
$ 移动到当前行的末尾
w 移动到下一个单词或标点符号的开头
W 移动到下一个单词的开头, 忽略标点符号
b 移动到上一个单词或标点符号的开头
B 移动到上一个单词的开头, 忽略标点符号
Ctrl-f 或 Page Down 向下翻一页
Ctrl-b 或 Page Up 向上翻一页
numberG 移动到第 number 行. 例如: 1G 移动到文件的第一行
g 移动到文件的开端
G 移动到文件末尾

为什么 h、j、k 和 l 按键被用来移动光标呢?

因为在开发 vi 之初,并不是所有的视频终端都有箭头按键,熟练的打字员可以使用规则的键盘按键来移动光标,他们的手从不需要移开键盘。

vi 中的许多命令都可以在前面加上一个数字,比方说上面提到的 "G" 命令,在命令之前加上一个数字,我们就可以指定命令执行的次数。

例如,命令 "5j" 导致 vi 向下移动 5 行。

基本编辑

大多数编辑工作由一些基本的操作组成,比如说插入文本,删除文本和通过剪切和粘贴来移动文本。

vi,当然以它自己的独特方式来支持所有的操作。

vi 也提供了有限的撤销形式;如果我们按下 "u" 按键,当在命令模式下,vi 将会撤销你所做的最后一次修改,当我们试着执行一些基本的编辑命令时,这会很方便。

追加文本

vi 有几种不同进入插入模式的方法。我们已经使用了 i 命令来插入文本,让我们返回到我们的 foo.txt 文件中, 呆一会儿。

The quick brown fox jumped over the lazy dog

如果我们想要在这个句子的末尾添加一些文本,我们会发现 i 命令不能完成任务,因为我们 不能把光标移到行尾。

vi 提供了追加文本的命令,明智地命名为 a 命令,如果我们把光标移动到行尾,输入 a 光标就会越过行尾,vi 进入插入模式。这样就允许我们添加更多的文本。

The quick brown fox jumped over the lazy dog. It was cool.

记住按下 Esc 按键来退出插入模式 。

因为我们几乎总是想要在行尾附加文本,所以 vi 提供了一种快捷方式来移动到当前行的末 尾,并且能添加文本,它是 A 命令。

试着用一下它,给文件添加更多行。首先,使用 "0"(零) 命令,将光标移动到行首;现在我们输入"A",来添加以下文本行:

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5 

再一次,按下 Esc 按键退出插入模式。

正如我们所看到的,大 A 命令非常有用,因为在启动插入模式之前,它把光标移到了行尾 。

打开一行

我们插入文本的另一种方式是 “打开” 一行;这会在存在的两行之间插入一个空白行,并且进入插入模式,这种方式有两个变体:

命令 打开行
o 当前行的下方打开一行
O 当前行的上方打开一行

我们可以演示一下:把光标放到 "Line 3" 上,按下小 o 按键

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3

line 4
line 5

在第三行之下打开了新的一行,并且进入插入模式。按下 Esc,退出插入模式。

按下 u 按键,撤销我们的修改。

按下大 O 按键在光标之上打开新的一行 。

The quick brown fox jumped over the lazy dog. It was cool.
Line 2

Line 3
Line 4
Line 5

按下 Esc 按键,退出插入模式,并且按下 u 按键,撤销我们的更改。

删除文本

正如我们期望的,vi 提供了各种各样的方式来删除文本,所有的方式包含一个或两个按键。

首先,x 按键会删除光标位置的一个字符,可以在 x 命令之前带上一个数字来指明要删除的字符个数。

d 按键更通用一些。

类似 x 命令,d 命令之前可以带上一个数字,来指定要执行的删除次数;另外,d 命令之后总是带上一个移动命令,用来控制删除的范围

这里有些实例

命令 删除的文本
x 当前字符
3x 当前字符及其后的两个字符
dd 当前行
5dd 当前行及随后的四行文本
dW 从光标位置开始到下一个单词的开头
d$ 从光标位置开始到当前行的行尾
d0 从光标位置开始到当前行的行首
从光标位置开始到文本行的第一个非空字符
dG 从当前行到文件的末尾
d20G 从当前行到文件的第 20 行

把光标放到第一行单词 "It" 之上,重复按下 x 按键直到删除剩下的部分。

下一步,重复按下 u 按键直到恢复原貌。

注意:真正的 vi 只是支持单层面的 undo 命令。

vim 则支持多个层面的。

我们再次执行删除命令,这次使用 d 命令。

还是移动光标到单词 "It" 之上,按下 dW 来删除单词 。

The quick brown fox jumped over the lazy dog. was cool.
Line 2
Line 3
Line 4
Line 5

按下 d$ 删除从光标位置到行尾的文本。

The quick brown fox jumped over the lazy dog.
Line 2
Line 3
Line 4
Line 5

按下 dG 按键删除从当前行到文件末尾的所有行。

~
.... 

连续按下 u 按键三次,来恢复删除部分。

剪切,复制和粘贴文本

这个 d 命令不仅删除文本,它还 "剪切" 文本。

每次我们使用 d 命令,删除的部分被复制到一个粘贴缓冲区中 (看作剪切板)。

稍后我们可以执行小 p 命令把剪切板中的文本粘贴到光标位置之后,或者是大 P 命令把文本粘贴到光标之前。

y 命令用来 "yank"(copy) [– "拉"(复制)] 文本,和 d 命令剪切文本的方式差不多。

这里有些把 y 命令和各种移动命令结合起来使用的实例。

命令 复制的内容
yy 当前行
5yy 当前行及随后的四行文本
yW 从当前光标位置到下一个单词的开头
y$ 从当前光标位置到当前行的末尾
y0 从当前光标位置到行首
从当前光标位置到文本行的第一个非空字符
yG 从当前行到文件末尾
y20G 从当前行到文件的第 20 行

我们试着做些复制和粘贴工作。

把光标放到文本第一行,输入 yy 来复制当前行。

下一步,把光标移到最后一行(G),输入小写的 p 把复制的一行粘贴到当前行的下面。

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5
The quick brown fox jumped over the lazy dog. It was cool.

和以前一样,u 命令会撤销我们的修改。

光标仍然位于文件的最后一行,输入大写的 P 命令把所复制的文本粘贴到当前行之上。

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
The quick brown fox jumped over the lazy dog. It was cool.
Line 5

试着执行上表中一些其他的 y 命令,了解小写 p 和大写 P 命令的行为。

当你完成练习之后,把文件恢复原样。

连接行

vi 对于行的概念相当严格,通常,不可能把光标移到行尾,再删除行尾结束符 (回车符) 来连接当前行和它下面的一行。

由于这个原因,vi 提供了一个特定的命令,大写的 J (不要与小写的 j 混淆了, j 是用来移动光标的) 把行与行之间连接起来。

如果我们把光标放到 line 3 上,输入大写的 J 命令,看看发生什么情况。

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3 Line 4
Line 5 

查找和替换

vi 能够把光标移动到搜索到的匹配项上。

vi 可以在单一行或整个文件中运用这个功能。

它也可以在有或没有用户确认的情况下实现文本替换。

查找一行

f 命令查找一行,移动光标到下一个所指定的字符上。

例如,命令 fa 会把光标定位到同一行中下一个出现的 "a" 字符上。

在一行中执行了字符的查找命令之后,通过输入 ";" 分号来重复这个查找。

查找整个文件

移动光标到下一个出现的单词或短语上,使用 / 命令。这个命令和我们之前在 less 程序中学到的一样。

当你输入 / 命令后,一个 "/" 字符会出现在屏幕底部。

下一步,输入要查找的单词或短语后,按下回车,光标就会移动到下一个包含所查找字符串的位置。

通过 n 命令来重复先前的查找。

这里有个例子:

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5

把光标移动到文件的第一行。

输入 /Line

然后键入回车。

光标会移动到第二行。

下一步,输入 n, 光标移到第三行

重复这个 n 命令,光标会继续向下移动直到遍历了所有的匹配项。

虽然目前我们只是使用了单词和短语来作为我们的查找模式,但是 vi 允许使用正则表达式,一种强大的用来表示复杂文本模式的方法。

我们将会在随后的章节里面详尽地介绍正则表达式。

全局查找和替代

vi 使用 ex 命令来执行查找和 替换(replace) 操作 (vi 中叫做 “substitution” [– 中文也是代替, 替换的意思])。

把整个文件中的单词“Line”,更改为“line”。

我们输入以下命令:

:%s/Line/line/g

我们把这个命令分解为几个单独的部分,看一下每部分的含义:

条目 含义
: 冒号字符运行一个 ex 命令
% 指定要操作的行数
% 是一个快捷方式, 表示从第一行到最后一行
另外, 操作范围也可以用数字代替
例如 1,5 (因为我们的文件只有 5 行文本)
或者用 1,$ 来代替, 意思是 "从第一行到文件的最后一行"
如果省略了文本行的范围, 那么操作只对当前行生效
s 指定操作
在这种情况下是, 替换 (查找与替代)
/Line/line 查找类型与替代文本
g 这是 "全局" 的意思
意味着对文本行中所有匹配的字符串执行查找和替换操作
如果省略 g, 则只替换每个文本行中第一个匹配的字符串

执行完查找和替代命令之后,我们的文件看起来像这样:

The quick brown fox jumped over the lazy dog. It was cool.
line 2
line 3
line 4
line 5

我们也可以指定一个需要用户确认的替换命令,通过添加一个 "c" 字符到这个命令的末尾来完成这个替换命令。

例如:

:%s/line/Line/gc

这个命令会把我们的文件恢复先前的模样。然而,在执行每个替换命令之前,vi 会停下来,通过下面的信息,来要求我们确认这个替换。

replace with Line (y/n/a/q/l/^E/^Y)?

括号中的每个字符都是一个可能的选择,如下所示。

按键 行为
y 执行替换操作
n 跳过这个匹配的实例
a 对这个及随后所有匹配的字符串执行替换操作
q 或者 esc 退出替换操作
l 执行这次替换并退出
l 是 "last" 的简写
Ctrl-e, Ctrl-y 分别是向下滚动和向上滚动
用于查看建议替换的上下文

如果你输入 y,则执行这个替换,输入 n 则会导致 vi 跳过这个实例,而移到下一个匹配项上。

编辑多个文件

同时能够编辑多个文件是很有用的,你可能需要更改多个文件或者从一个文件复制内容到另一个文件。

通过 vi,我们可以打开多个文件来编辑,只要在命令行中指定要编辑的文件名

vi file1 file2 file3...

我们先退出已经存在的 vi 会话,然后创建一个新文件来编辑。

输入 :wq 来退出 vi 并且保存所做的修改。

下一步,我们将在 home(家) 目录下创建一个额外用来玩耍的文件,通过获取从 ls 命令的输出,来创建这个文件

ls -l /usr/bin > ls-output.txt

用 vi 来编辑我们的原文件和新创建的文件。

vi foo.txt ls-output.txt

vi 启动,我们会看到第一个文件显示出来。

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5

文件之间转换

从这个文件转到下一个文件,使用这个 ex 命令:

:n

回到先前的文件使用:

:N

当我们从一个文件移到另一个文件时,如果当前文件没有保存修改,vi 会阻止我们转换文件,这是 vi 强制执行的政策。

在命令之后添加感叹号,可以强迫 vi 放弃修改而转换文件。

另外,上面所描述的转换方法,vim (和一些版本的 vi) 也提供了一些 ex 命令,这些命令使多个文件更容易管理。

我们可以使用 :buffers 命令查看正在编辑的文件列表。

运行这个命令后,屏幕顶部就会显示出一个文件列表。

:buffers
1 %a "foo.txt" line 1
2 "ls-output.txt" line 0
Press ENTER or type command to continue

转换到其它缓冲区(文件), 输入 :buffers 缓冲区数字。

例如,从包含 foot.txt 文件的缓冲区1,转换到包含 ls-output.txt 文件的缓冲区2,应该输入

:buffer 2

我们的屏幕上将会显示的是第二个文件。

打开编辑额外的文件

当我们正在编辑文件时,同样可以打开其它文件加入进来。

ex 的 :e 命令("edit" 的缩写)后面跟着一个想要打开的额外文件名。

我们先退出目前的编辑模式,返回到命令行,使用 vi 只打开一个文件:

vi foo.txt

添加第二个文件,输入命令:

:e ls-output.txt

它会显示在屏幕上,并且,通过验证,第一个文件仍然存在。

:buffers
1 # "foo.txt" line 1
2 %a "ls-output.txt" line 0
Press ENTER or type command to continue

注意:你不能通过 :n 或 :N 命令在由 :e 命令加载的文件之间进行切换。

这时要使用 :buffer 命令,其后加上缓冲区号码,来转换文件。

从一个文件复制内容到另一个文件

当我们编辑多个文件时,经常地要复制文件的一部分到另一个正在编辑的文件。

使用之前我们学到的拉 (yank) 和粘贴命令,这很容易完成。

以打开的两个文件为例,首先转换到缓冲区 1 (foo.txt), 输入:

:buffer 1

我们应该得到以下输出:

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5

下一步, 把光标移到第一行,并且输入 yy 来复制这一行。

转换到第二个缓冲区, 输入:

:buffer 2

现在屏幕会包含一些文件列表 (这里只列出了一部分):

total 343700
-rwxr-xr-x 1 root root 31316 2007-12-05 08:58 [
-rwxr-xr-x 1 root root 8240 2015-12-09 13:39 411toppm
-rwxr-xr-x 1 root root 111276 2016-01-31 13:36 a2p
-rwxr-xr-x 1 root root 25368 2014-10-06 20:16 a52dec
-rwxr-xr-x 1 root root 11532 2015-05-04 17:43 aafire
-rwxr-xr-x 1 root root 7292 2015-05-04 17:43 aainfo

移动光标到第一行,输入 p 命令把我们从前面文件中复制的一行粘贴到这个文件中:

total 343700
The quick brown fox jumped over the lazy dog. It was cool.
-rwxr-xr-x 1 root root 31316 2015-12-05 08:58 [
-rwxr-xr-x 1 root root 8240 2015-12-09 13:39 411toppm
-rwxr-xr-x 1 root root 111276 2016-01-31 13:36 a2p
-rwxr-xr-x 1 root root 25368 2014-10-06 20:16 a52dec
-rwxr-xr-x 1 root root 11532 2015-05-04 17:43 aafire
-rwxr-xr-x 1 root root 7292 2015-05-04 17:43 aainfo

插入整个文件到另一个文件

也有可能把整个文件插入到我们所编辑的文件中。

看一下实际操作,结束 vi 会话,重新启动一个只打开一个文件的 vi 会话。

vi ls-output.txt

再一次看到我们的文件列表:

total 343700
-rwxr-xr-x 1 root root 31316 2015-12-05 08:58 [
-rwxr-xr-x 1 root root 8240 2015-12-09 13:39 411toppm
-rwxr-xr-x 1 root root 111276 2016-01-31 13:36 a2p
-rwxr-xr-x 1 root root 25368 2014-10-06 20:16 a52dec
-rwxr-xr-x 1 root root 11532 2015-05-04 17:43 aafire
-rwxr-xr-x 1 root root 7292 2015-05-04 17:43 aainfo

移动光标到第三行,然后输入以下 ex 命令:

:r foo.txt

这个 :r 命令 (是 "read" 的简称) 把指定的文件插入到光标位置之前。

现在屏幕应该看起来像这样:

total 343700
-rwxr-xr-x 1 root root 31316 2015-12-05 08:58 [
-rwxr-xr-x 1 root root 8240 2015-12-09 13:39 411toppm
The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5
-rwxr-xr-x 1 root root 111276 2016-01-31 13:36 a2p
-rwxr-xr-x 1 root root 25368 2014-10-06 20:16 a52dec
-rwxr-xr-x 1 root root 11532 2015-05-04 17:43 aafire
-rwxr-xr-x 1 root root 7292 2015-05-04 17:43 aainfo

保存工作

像 vi 中的其它操作一样,有几种不同的方法来保存我们所修改的文件。

我们已经研究了 :w 这个 ex 命令, 但还有几种方法,可能我们也觉得有帮助,在命令模式下,输入 ZZ 就会保存并退出当前文件。

同样地, ex 命令 :wq 把 :w 和 :q 命令结合到一起来完成保存和退出任务。

这个 :w 命令也可以指定可选的文件名。

这个的作用就如 "另存为"。

例如,如果我们正在编辑 foo.txt 文件,想要保存一个副本,叫做 foo1.txt,那么我们可以执行以下命令

:w foo1.txt

注意:当上面的命令以一个新名字保存文件时,但它并没有更改你正在编辑的文件的名字,如果你继续编辑的话,你还是在编辑文件 foo.txt,而不是 foo1.txt。

拓展阅读

即使把这章所学的内容都加起来,我们也只是学了 vi 和 vim 的一点儿皮毛而已。

这里有一些在线的资料,你可以用来继续 vi 学习之旅。


参考:

happypeter/TLCL

The Linux Command Line