第十二章 vi 文本编辑器

前言

  1. 概述
  • 现在这个时代存在着很多图形界面编辑器和易用的基于文本的编辑器,例如 nano ,那为什么还要学习 vi ?这里有三条充分理由:
    • vi 总是可用的。如果用户面前系统没有图形界面,例如远程服务器或者是本地系统的 X 配置不可用,那么 vi 就会成为救命稻草。
    • 尽管 nano 已经得到了越来越广泛的应用,但是,迄今为止它还不是通用的。而 POSIX (一种 UNIX 系统的程序兼容标准) 则要求系统必须配备有 vi 。
    • vi 是轻量级的软件,运行速度快。对很多的任务来说,启动 vi 比在菜单中找到一个图形界面编辑器并等待几兆大小的编辑器载入要容易得多。另外,vi 的设计还非常利于打字。

vi 背景

  1. 1976年,加州大学生伯克利分校的学生,之后成为 Sun 公司创始人之一的 Bill Joy 写出了 vi 的第一个版本。
  2. vi 出自单词 visual ,含义是能够在视频终端上移动光标来进行编辑。
  3. 在图形界面编辑器出现之前是行编辑器的天下,用户每次只能在一行文本上进行编辑。
  4. 使用行编辑器的时候,用户需要告知编辑器是在哪一行进行什么样操作。比如添加或者删除。
  5. 视频终端 (而非基于打印机的终端,比如电报) 的来临使得全屏幕的编辑成为了可能。
  6. 由于 vi 融合了强大的行编辑器 ex ,vi 用户也可以同时使用行编辑命令。
  7. 大多数 Linux 发行版配备的并不是真正的 vi ,而是 Bram Moolenaar 编写的 vi 加强版 vim 。
  8. vim 是传统 UNIX 系统中 vi 的实质性改良版。
  9. 通常, vim 的硬链接 (或别名) 指向 Linux 系统的 vi 名称。

vi 启动和退出

  1. 输入以下命令启动 vi :
    1
    [me@linuxbox ~]$ vi
  2. 像操作 nano 一样,现在应该先学习如何退出 vi 。输入以下命令退出 vi (需要注意的是,冒号是命令的一部分) 。
    1
    :q
  3. 此时 shell 会返回初始的操作窗口,如果因为一些原因,vi 不能够退出 (通常是因为没有保存修改过的文件) ,可以通过在命令后添加感叹号的方式强制退出 vi 。
    1
    :q!
  4. 如果用户不能确定 vi 所处的状态,可以按 Esc 键两次返回初始状态。

编辑模式

  1. vi 编辑模式
  • 再次启动 vi ,并向其传递一个不存在的文件名,就可以通过 vi 创建新文件。
    1
    2
    [me@linuxbox ~]$ rm -f foo.txt
    [me@linuxbox ~]$ vi foo.txt
  • vi 是一个模态编辑器。 vi 启动后进入的是命令模式。
  1. 进入插入模式
  • 如果用户需要向文件中添加一些内容,那么首先要做的就是按 I 键进入插入模式。
  • 若此时 vim 是在增强模式下正常地运行,那么在屏幕底部会出现以下内容 (若 vim 以兼容模式运行,则不会出现) :
    1
    --INSERT--
  • 现在用户可以进行输入操作了,例如:
    1
    The quick brown fox jumped over the lazy dog.
  • 最后按 Esc 退出插入模式并返回命令模式。
  1. 保存工作
  • 要保存用户修改过的文件,在命令模式下输入一条 ex 命令,也就是按 : 键。
  • 这样之后,一个冒号之后输入 w ,如下所示:
    1
    :w
  • 文件写入硬盘驱动器之后,用户会在屏幕底部得到一条确认的信息。
    1
    "foo.txt" [New] 1L, 46C written
  • 如果用户阅读 vim 的说明文档,会困惑地发现命令模式被称为普通模式,而使用 ex 命令则被称为命令模式。

移动光标

  1. 基本介绍
  • 在命令模式下, vi 提供了很多移动光标命令,其中有一些命令是与 less 命令共用的。
  • 为什么使用 H、 J、 K 和 L 键来移动光标呢?这是因为在 vi 最初出现的阶段,并不是所有的视频终端都有方向键,这样的设计使得 vi 高手可以手不离键盘地移动光标。
  • 许多 vi 的命令的前面都可以缀上数字。前缀数字可以控制命令执行的次数,比如 5j 可以使得光标下移 5 行。
  1. vi 移动光标
光标
L 或 右方向键 右移一位
H 或 左方向键 左移一位
J 或 下方向键 下移一行
K 或 上方向键 上移一行
数字 0 至本行开头
Shift-6(^) 至本行第一个非空字符
Shift-4($) 至本行的末尾
W 至下一单词或标点的开头
Shift-W(W) 至下一单词的开头,忽略标点
B 至上一单词或标点的开头
Shift-B(B) 至上一个单词的开头,忽略标点
Ctrl-F 或 Page Down 下翻一页
Ctrl-B 或 Page UP 上翻一页
number-Shift-G 至第 number 行(如 1G 将光标移到文件的第一行)
Shift-G(G) 至文件的最后一行

基本编辑

基本介绍

  1. 插入、删除、剪切、复制等构成了基本的文本编辑操作, vi 也以其特殊的方式支持这些操作。
  2. 同时 vi 还支持有限的撤销操作,在命令模式下按 U 键就可以撤销用户最后一步操作。

添加文本

  1. 有几种方式都可以进入 vi 的插入模式。现在假设已经使用 i 命令进入插件模式。
  2. 先回顾下 foo.txt 内容
    1
    The quick brown fox jumped over the lazy dog.
  3. 因为光标不能跳出行末,所以单纯使用 i 命令并不能完成在文本末尾添加内容的任务。
  4. 为此 vi 提供了在行末添加文本的 a 命令。当用户将光标移动到行的末尾并使用 a 命令时,光标会越过文本的末尾,同时 vi 进入插入模式。
  5. 这样用户就可以在行末添加文本了。
    1
    The quick brown fox jumped over the lazy dog. It was cool.
  6. 输入结束后不要忘记按 Esc 退出插入模式。
  7. 因为用户经常用到在行末添加文本的功能,所以 vi 提供了使光标移动到行末并进行插入模式的快捷方式 -A 命令。
  8. 首先,使用 0 命令将光标移动到行的开头。接下来使用 A 命令将以下内容写入文件中。
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. It was cool.
    line2
    line3
    line4
    line5
  9. 按 Esc 键退出插入模式。可以看到,A 命令使 vi 进入插入模式并自动将光标移动到行尾,非常好用。

插入一行

  1. 基本介绍
  • 插入文本的另一种方式是在文本中重开一行,即在两行现存的文字中间插入空白行并进入插入模式。
  1. vi 插入一行
命令 开行
o 当前行上方
O 当前行的下方
  1. 案例
  • 下面这个例子示范了这两种命令的作用。先将光标置于 Line 3 ,在输入 O ,结果如下所示。
    1
    2
    3
    4
    5
    6
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2
    Line 3

    line 4
    line 5
  • 我们可以看到在第三方的下方 vi 插入了一行,并进入了插入模式。按 Esc 键退出插入模式。按 Esc 键退出插入模式,按 u 键取消上述操作。
  • 继续输入命令 o ,就会在第三行的上方插入了一行。
    1
    2
    3
    4
    5
    6
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2

    Line 3
    line 4
    line 5
  • 再次按 Esc 键退出插入模式并按 u 键取消操作。

删除文本

  1. 基本介绍
  • 就像用户期待的一样, vi 提供了很多种删除文本的方式,每一种都需要进行一次至两次的按键操作。
  • 首先,X 键会删除光标处的字符。x 命令可以加以数字前缀来明确删除的字符数目。D 键则使用得更加普遍。
  • 像 x 命令一样,d 命令也可加练数字前缀来明确删除次数。另外,d 命令总是加以控制删除范围的光标移动命令作为后缀。
  1. vi 文本删除
命令 删除内容
x 当前字符
3x 当前字符和之后 2 个字符
dd 当前行
5dd 当前行和之后 4 行
dW 当前字符到下一单词的起始
d$ 当前字符到当前行的末尾
d0 当前字符到当前行的起始
d^ 当前字符到当前行下一个非空字符串
dG 当前行到文件末尾
d20G 当前行到文件第 20 行
  1. 案例
  • 现在让我们来练习一下使用 d 命令。我们再次将光标移动到单词 It ,使用 dW 命令来删除整个单词。
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. was cool.
    Line 2
    Line 3
    line 4
    line 5
  • 使用的的 d$ 删除光标至本行末尾的字符。
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog.
    Line 2
    Line 3
    line 4
    line 5
  • 使用 dG 删除当前行到文件末尾的内容。
    1
    2
    3
    4
    ~
    ~
    ~
    ~
  • 可以使用 u 命令三次来取消之前的操作。

剪切、复制和粘贴文本

  1. 基本介绍
  • 命令 d 不只是删除文本,而是在剪切文本。用户每次使用 d 命令之后,都会复制删除的内容进缓存 (类似剪切板) ,然后用户就可以使用 p 命令将缓存中的内容粘贴到光标之后或使用 p 命令将内容粘贴到光标之前。
  • 就像命令 d 剪切文本的形式一样,命令 y 会复制文本。
  1. vi 复制命令
命令 复制内容
yy 当前行
5yy 当前行和之后 4 行
yW 当前字符到下一个单词的起始
y$ 当前字符到当前行的末尾
y0 当前字符到当前行的起始
y^ 当前字符到当前行下一个非空字符
yG 当前行到文件末尾
y20G 当前行到文件第 20 行
  1. 案例
  • 现在让我们来练习一下复制和粘贴。我们将光标移至文本的第一行,使用 yy 命令复制当前行。接下来,将光标移至最后一行 (G) ,使用 p 命令将复制的内容粘贴到当前行的下方。
    1
    2
    3
    4
    5
    6
    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 命令将文本粘贴到当前行的上方。
    1
    2
    3
    4
    5
    6
    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

合并行

  1. 基本介绍
  • vi 在行的概念上非常严格。通常来说,将光标移动到行的末端并删除行的末尾字符并不能将此行合并。
  • 因此,vi 专门提供了 J 命令 (不要与移动光标的 j 命令混淆) 来合并行。
  1. vi 合并行
  • 若将光标置于第 3 行并输入 J 命令,将得到如下结果。
    1
    2
    3
    4
    The quick brown for jumped over the lazy dog. It was cool.
    Line 2
    Line 3 Line 4
    Line 5

查找和替换

基本介绍

  1. vi 提供了在一行或者整个文件中,根据需要将文件移动至指定位置的功能。
  2. vi 还可以执行文本替换工作,用户可指定替换时是否需要用户确认。

行内搜索

  1. 命令 f 在行内进行搜索,并将光标移至搜索到的下一个指定字符。比如,命令 fa 就会将光标移动到本行下移除字符 a 的地方。
  2. 在执行过一次行内搜索之后,输入分号可以使 vi 重复一次搜索。

搜索整个文件

  1. 基本介绍
  • 同第 3 章中讲解过的 less 程序一样,命令 / 可以完成对单词或短语的搜索。
  • 当用户使用 / 命令后,一个 / 符号会出现在屏幕的底部。接下来,输入需要搜索的单词或短语,Enter 结束。
  • 这时光标就会移动到下一次处包含被搜索字符串的地方,使用 n 命令可以重复此搜索。
  1. 案例
  • 文件如下:
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2
    Line 3
    Line 4
    Line 5
  • 将光标移至文件的第一行,并输入如下代码。
    1
    /Line
  • 输入 Enter 以结束,光标将移动至第 2 行。接下来,输入 n ,光标将继续移动至第 3 行。重复输入 n 直至光标移动到文档的最后,且找不到符合条件的字符串。
  • 尽管现在只讲解到 vi 的单词和词组的搜索模式,但是 vi 同样支持正则表达式 (一种强大的表达复杂文本模式的方法) 的应用。

全局搜索和替换

  1. 基本介绍
  • vi 使用 ex 命令来执行几行内或者整个文件中的搜索和替换操作。
  • 输入以下命令可将文件中的 Line 替换为 line 。
    1
    :%s/Line/line/g
  1. vi 全局搜索和替换语法
组成 含义
: 分号用于启动一条 ex 命令
% 确定了操作作用范围。 % 简洁地代表了从文件的第一行到最后一行。本命令的范围还可以表示为 1,5 (因为本文件只有 5 行) ,或者是 1,$ ,意思是从第 1 行到文件最后一行。如果不明确指出命令的作用范围,那么命令只会在当前行生效
s 指定了具体的操作-本次是替换操作 (搜索和替换)
/Line/line 搜索和替换的文本
g 代指 global (全局) ,也就是说对搜索到的每一行的每一个实例,进行替换。如果 g 缺失,那么只替换每一行第一个符合条件的实例
  1. 案例
  • 以下是执行过查找和替换命令之后的文档内容。
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2
    Line 3
    Line 4
    Line 5
  • 在命令末尾添加 c ,则命令在每次替换之前都会请求用户确认。如下所示:
    1
    :%s/line/Line/gc
  • 此命令将会将文件替换回原来的样子,但是每次替换前,vi 都会停下来询问用户是否确认执行替换。
  1. vi 替换确认功能键
功能键 执行
y 执行替换
n 跳过此次替换
a 执行此次替换和之后的所有替换
q 或者 Esc 停止替换
l 执行此次替换并退出替换,last 的缩写
Ctrl-E,Ctrl 分别是向下滚动和向上滚动,能用于查看替换处的上下文

编辑多个文件

基本介绍

  1. 用户经常遇到需要同时编辑多个文件的情况。可能是需要对多个文件作出修改,或者是拷贝文件的部分内容到另一个文件。用户可以通过在命令行具体指定多个文件的方式使 vi 打开多个文件。
    1
    vi file1 file2 file3...
  2. 现在退出所处的 vi 会话,并创建一个用于编辑的新文件,输入 :wq 来退出 vi 并保存做出的修改。
  3. 接下来,使用 ls 命令的部分输出在主目录创建一个用于实现的新文件。
    1
    [me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt
  4. 现在就用 vi 来同时编辑旧文件和新文件。
    1
    [me@linuxbox ~]$ vi foo.txt ls-output.txt
  5. vi 启动后,屏幕显示内容如下所示:
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2
    Line 3
    Line 4
    Line 5

切换文件

  1. 使用一下 ex 命令来从一个文件切换到下一个文件。
    1
    :n
  2. 切换回上一个文件。
    1
    :N
  3. 当用户从一个文件切换到另一个的时候,vi 要求用户必须先保存对当前文件做出的修改才能切换到其他文件。
  4. 若要放弃对文件的修改并使 vi 强制切换到另一个文件,可在命令后添加感叹号 ! 。
  5. 除了以上描述的切换方法之外,vim (和一些版本的 vi) 还提供了一些 ex 命令让用户可以更轻松地编辑多个文本。用户可使用 :buffers命令来查看正在编辑的文件列表。
    1
    2
    3
    4
    :buffers
    1 %a "foo.txt" line 1
    2 "ls-output" line 0
    Press ENTER or type command to continue
  6. 输入 :buffer 加文件编号可切换到另一个问卷。如从文件 1 (foo.txt) 切换到文件 2 (ls-output.txt) ,用户应当输入如下命令。
    1
    :buffer 2
  7. 现在屏幕展示的就是文件 2 的内容了。

载入更多的文件

  1. 我们也可以在现有的编辑会话中载入更多的文件。使用 ex 命令 :e 加文件名可以载入另一个文件。
  2. 先退出现有的编辑会话并回到命令行模式。重启 vi,并只打开一个文件。
    1
    [me@linuxbox ~]$ vi foo.txt
  3. 添加一个文件到编辑会话中,输入下列代码。
    1
    :e ls-output.txt
  4. 屏幕将展示第二个文件的内容,而第一个文件仍然处在编辑状态,可使用 buffers 命令来证实。
    1
    2
    3
    4
    :buffers
    1 %a "foo.txt" line 1
    2 "ls-output" line 0
    Press ENTER or type command to continue
  5. 使用 :ez 载入的文件不会响应 :n 或者 :N 命令,而需使用 :buffer 加文件编号来切换文件。

文件之间的内容复制

  1. 用户在编辑多个文件的过程,有时会需要将一个文件中的一部分复制到另一个文件中。使用之前使用过的复制粘贴命令即可完成此功能。
  2. 首先,在载入的两个文件中,切换到文件 1 (foo.txt) 。
    1
    :buffer 1
  3. 此时屏幕显示如下所示:
    1
    2
    3
    4
    5
    The quick brown fox jumped over the lazy dog. It was cool.
    Line 2
    Line 3
    Line 4
    Line 5
  4. 接下来,将光标移动到文件的第一行并输入 yy 命令来复制第一行。
  5. 输入如下命令以切换到文件 2。
    1
    :buffer 2
  6. 现在屏幕将会显示一份文件列表,如下所示:
    1
    2
    3
    4
    5
    6
    7
    total 343700
    -rwxr-xr-x 1 root root 31316 2011-12-05 08:58 [
    -rwxr-xr-x 1 root root 8240 2011-12-09 13:39 411toppm
    -rwxr-xr-x 1 root root 111276 2012-01-31 13:36 a2p
    -rwxr-xr-x 1 root root 25368 2010-10-06 20:16 a52dec
    -rwxr-xr-x 1 root root 11532 2011-05-04 17:43 aafire
    -rwxr-xr-x 1 root root 7292 2011-05-04 17:43 aainfo
  7. 将光标移动到文件的第一行并使用 p 命令将从文件 1 复制的内容粘贴到本文件。
    1
    2
    3
    4
    5
    6
    7
    8
    total 343700
    The quick brown fox jumped over the lazy dog. It was cool.
    -rwxr-xr-x 1 root root 31316 2011-12-05 08:58 [
    -rwxr-xr-x 1 root root 8240 2011-12-09 13:39 411toppm
    -rwxr-xr-x 1 root root 111276 2012-01-31 13:36 a2p
    -rwxr-xr-x 1 root root 25368 2010-10-06 20:16 a52dec
    -rwxr-xr-x 1 root root 11532 2011-05-04 17:43 aafire
    -rwxr-xr-x 1 root root 7292 2011-05-04 17:43 aainfo

插入整个文件

  1. 用户还可以将一个文件完全插入正在编辑的文件中。为了实际演示这项功能,先结束现有的 vi 会话并重启 vi 的同时只打开一个文件。
  2. 屏幕将再次显示一份文件列表。
    1
    2
    3
    4
    5
    6
    7
    total 343700
    -rwxr-xr-x 1 root root 31316 2011-12-05 08:58 [
    -rwxr-xr-x 1 root root 8240 2011-12-09 13:39 411toppm
    -rwxr-xr-x 1 root root 111276 2012-01-31 13:36 a2p
    -rwxr-xr-x 1 root root 25368 2010-10-06 20:16 a52dec
    -rwxr-xr-x 1 root root 11532 2011-05-04 17:43 aafire
    -rwxr-xr-x 1 root root 7292 2011-05-04 17:43 aainfo
  3. 将光标移动到文件的第三行并输入如下 ex 命令。
    1
    :r foo.txt
  4. 命令 :r 将指定的文件内容插入到光标位置之前。现在屏幕显示如下所示:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    total 343700
    -rwxr-xr-x 1 root root 31316 2011-12-05 08:58 [
    -rwxr-xr-x 1 root root 8240 2011-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 2012-01-31 13:36 a2p
    -rwxr-xr-x 1 root root 25368 2010-10-06 20:16 a52dec
    -rwxr-xr-x 1 root root 11532 2011-05-04 17:43 aafire
    -rwxr-xr-x 1 root root 7292 2011-05-04 17:43 aainfo

保存工作

  1. 就像其他功能一样,vi 提供了很多种方式来保存编辑过的文件。前面的章节已经介绍过用于此功能的 ex 命名 :w ,但是还有一些其他可用的方法。
  2. 在命令模式下,输入 ZZ 将保存当前文档并退出 vi 。同样的, ex 命令 :wq 组合了 :w 和 :q 这两个命令的功能,能够保存文件并退出 vi 。
  3. 当命令 :w 指定一个随意的文件名时,命令的功能就类似于另存为。
  4. 例如,用户在编辑 foo.txt 的时候想要将其另存为 foo1.txt ,那么就可以输入如下内容。
    1
    :w foo1.txt
  5. 此命令在以新名称保存文件的同时,并不更改编辑中的原文件的名称。当用户继续编辑时,编辑的还是 foo.txt 而不是 foo1.txt 。

参考文章

  • 转载:Linux 命令行大全