原文: How to Exit Vim – Vim Save and Quit Command Tutorial

从我开始编程的第一年起,我就一直在使用 Vim。不管使用什么 IDE/编辑器,Vim 插件总是我安装的第一个插件。

我知道,对于不熟悉 Vim 的人来说,使用 Vim 可能是一种挑战。在这篇文章中,我们将介绍一些基本的话题,比如如何退出(exit) Vim,而不是放弃(quit) Vim。

我还将介绍你可以用来使用相应帮助文档的命令。要做到这一点,首先我们需要按几次 ESC 键,然后运行所提供的命令,例如,:h vim-modes,然后按 Enter

概述——如何退出 Vim

  1. 按一次 ESC 键(有时候是两次)
  2. 确保你使用的是英文输入法
  3. 下一步取决于当前的状态和你的期望:
  • 如果你没有做任何改变,输入 :q,然后按 Enter/return
  • 如果你做了一些改变,并希望保留它们,输入 :wq 并按 Enter/return
  • 如果你做了一些修改,并希望放弃它们,请输入 :q! 并按 Enter/return

如果你想更详细地了解它是如何工作的,让我们深入了解一下。

什么是 Vim 中的模式?

Vim 中有七种基础工作模式和另外七种被认为是基础模式的变种的模式。如果你想知道更多,你可以在 Vim 中运行 :h vim-modes 来阅读文档。

幸运的是,我们不需要知道所有这些模式就可以开始工作。但有三种模式是我们需要注意的:普通模式Normal Mode)、插入模式Insert Mode) 和 命令行模式Command-line Mode)。

什么是 Vim 中的普通模式?

普通模式 是至关重要的,因为只有在 普通模式 下我们才能运行命令(也有例外,但这不在本文的讨论范围之内)。

比如,如果我们想阅读 vim-modes 的文档,那么我们必须首先确定我们是在 普通模式 下,然后再输入 :h vim-modes。而进入 Normal Mode 最经典的方式就是按 ESC 键。即使你已经进入了 Normal Mode,按 ESC 也会让你保持在 Normal Mode,所以不用担心。

这个命令在文档中是这样的::h Normal, :h mode-switching

什么是 Vim 中的插入模式?

你使用 插入模式 来编辑当前文件(在 Vim 中,这通常被称为 buffer)。默认情况下,我们在打开一个文件后就处于 Normal Mode。如果我们需要对当前文件进行修改,首先我们需要切换到 insert Mode

最常见的切换方式是在 Normal Mode 下,将光标导航到我们要编辑的地方后按 i 键。

其实也有很多其他的方法可以进入 Insert Mode,比如 oOaAI 等等。

这个命令在文档中是这样的::h Insert, :h i_<Esc>, :h o, :h O, :h a, :h A, :h I

什么是 Vim 中的命令行模式?

命令行模式通常是一种“短期”模式,你使用它来运行 “Ex 命令”(不要与普通模式中的“命令”混淆)。

有趣的事实:Vim 实际上是 Vi Improved 的缩写,它基于另一个名为 vi 的文本编辑器。vi 基于一个名为 ex 的行编辑器。viex 都是由 Bill Joy 开发的。

对于 macOS 用户来说,另一个有趣的事实是:macOS 上只有 Vim,命令 viVim 的符号链接(symlink)。需要注意的是,命令行模式下的 “Ex command” 与普通模式下的 “command” 是不同的。

你可以在普通模式下输入 : 进入命令行模式。例如,如果你运行上面的命令来查看文档,只要你输入 :,那么你实际上是在使用命令行模式

同样,如果你想切换回正常模式,按 ESC

这个命令在文档中是这样的::h Command-line:h cmdline-lines

如何退出 Vim

image-169
documentation of :h quit

如果你查看一下 :h quit 的文档,会注意到 quit 的前缀是 :。这意味着 quit 是在命令行模式中使用的。

为了进入命令行模式,首先我们需要确保处于普通模式,然后只需输入 :。现在我们进入了命令行模式,只需输入 quit 并按 Enterreturn

你可能还会注意到 :q,它实际上是 :quit 命令的简写版本。也就是说,我们也可以通过执行 :q 来退出 Vim。

不过,它提到,“当做出更改且 Vim 拒绝放弃当前 buffer”时,该命令将失败(如果你还记得,buffer 只是打开的文件,没什么特别的)。

在这种情况下,我们需要使用 :wq,这意味着“写入并退出”。你可以在文档 :h:wq 下找到它。

:x 是另一个与 :wq 几乎相同的命令。根据 :h:x,它是“类似于 :wq,但只在做出更改时写入”。

这里的区别在于,无论你是否对其进行了更改,:wq 将保存文件。如果你没有做任何更改,:x 将不会保存文件。这里重要的是文件的修改时间,因为 :wq 会更新修改时间,而 :x 不会。

有些时候,我们宁愿不保留我们所做的更改。此时,我们需要 :q! 命令。! 通常被称为 “bang”,这使得 q! 就像是“强制退出”。

请注意,如果使用这种方式,你将丢失对该文件所做的所有更改,而且几乎没有办法恢复它们。

如何解决退出 Vim 的问题

如果我不想进入命令行模式怎么办?

没问题。在 Normal Mode 下,你也可以按 ZZZQ 来退出 Vim。其中 ZZ:x 相同,ZQ:q! 相同。

在这里,ZZZQ 被认为是 Normal Mode 的命令,而 :x:q! 是 Ex 命令。

如果 :wq 失败了怎么办?

这是可能的,因为当文件是只读的或者文件名丢失时,:wq 可能会失败。

请注意,当一个文件是只读的,Vim 不会阻止你打开和编辑该文件。你可能也会发现 :wq! 在那个时候也不能工作。你可能最终会放弃所有用 :q! 进行的修改,然后打开同一文件,以 sudo 为前缀,再做一次同样的修改。

你可以做的一件事是把这个文件保存在另一个你有写权限的目录下,比如 ~ 或者甚至是 /tmp,然后把它移回你没有写入权限的目录。

为了实现这一点,你可以运行 :w ~/my-backup-file,这将把该文件保存在 ~

接下来,你可以使用 mv 命令移动这个文件。还要注意,当 Vim 抱怨文件名丢失时,你需要这样做。

Vim 内部还有另一个解决方案。你可以运行以下命令::w !sudo tee %!sudo 表示在 shell 环境中运行 sudo 命令。

例如,:ls 将列出 Vim 中的所有缓冲区,但是 :!ls 将从 shell 运行 ls 命令并显示结果。tee 从标准输入(又名 stdin)复制到标准输出(又名 stdout)。% 为当前文件名。

这意味着我们从 shell 环境中使用 sudo 命令,从 Vim 复制当前文件(注意:修改过的版本)的内容,并将修改过的内容重定向到文件(通过使用文件名引用)。

image-170
文件内容更改警告

你会注意到上面的警告。发生此警告是因为在 shell 环境中更新了文件内容而没有引起 Vim 的注意。所以 Vim 认为这是一个外部变化,并让你知道发生了什么。

此时只需按 Enter,因为你是有意进行外部更改的。

你可能还会注意到,修改后的文件内容也显示在警告消息的上方。这是意料之中的,因为它来自标准输出 stdout。如果你不想看到它,你可以执行 :w !sudo tee% >/dev/null,这将丢弃来自 teestdout

如果我需要强制退出怎么办?

image-171

如果我们尝试按 control + c 退出,Vim 会显示上述信息。但你仍然可以通过在 Windows 上按 Ctrl + Alt + Delete 或者在 macOS 上按 Force Quit... 来解决这个问题。

下一次,当你试图再次打开同一个文件时,你应该看到这个(这里我用一个名为 foo.txt 的文件作为例子)。

image-173
swap file

不要惊慌——这是预料之中的事,因为 Vim 正试图帮助你恢复你可能丢失的宝贵修改。

通过检查该目录,你会发现一个扩展名为 .swp 的文件。这是一个 swap 文件(文档::h swap-file)。

image-174
swap file .swp

如果我们按 R 键恢复,我们将看到以下情况:

image-177
.swp recover

Enter 键后,现在你会发现你之前所做的修改又回来了。在你完成恢复过程后,你可以简单地删除 .swp 文件,这样你就不会再看到上面的错误。

总结

在这篇文章中,我们介绍了一些关于 Vim 模式的基本知识以及如何退出 Vim。我们还学习了如何解决退出 Vim 时的常见问题。

谢谢你阅读本文。谈到 Vim,还有很多东西需要学习。但如果你想了解更多,请随时探索 Vim 的帮助文档。