Vim 的问题是你不理解 vi

Your problem with Vim is that you don't grok vi

We can use m to move lines around, and j to join lines. For example if you have a list and you want to separate all the stuff matching (or conversely NOT matching some pattern) without deleting them, then you can use something like: :% g/foo/m$ ... and all the "foo" lines will have been moved to the end of the file. (Note the other tip about using the end of your file as a scratch space). This will have preserved the relative order of all the "foo" lines while having extracted them from the rest of the list. (This would be equivalent to doing something like: 1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d (copy the file to its own tail, filter the tail through grep, and delete all the stuff from the head).

正在阅读 Jim Dennis 的 this legendary answer,但我仍然无法理解这个序列:

1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d

哪位帮忙说说,什么是GGmap?为什么1G之间有一个爆炸! GG地图? Ggrep 是否来自 vim-fugitive?

除了 !GG 位之外,该命令中的所有内容都有意义。让我们解构整个事情:

1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d
  1. 1G 将光标移动到第 1 行,参见 :help G
  2. !GG 没有意义,
  3. ma 创建标记 a,参见 :help m
  4. p将未命名寄存器的内容放在光标后,见:help p,
  5. !Ggrep foo<ENTER>用外部命令grep foo过滤从当前行到最后一行的行,见:help !,
  6. 1G 将光标移动到第 1 行,
  7. :1,'a g/foo'/d 将匹配 foo' 的每一行从第 1 行剪切到标记 a,参见 :help :range:help :g:help d

我认为第 2 步是一个错字。 !GG 字面意思是“用外部命令 G 过滤从当前行到最后一行的文本”,这是没有意义的,因为 a) G 不是一个常见的 Unix 命令(如果它甚至存在),并且 b) 它后面没有 <ENTER>,这是执行 Ex 命令所必需的。

答案正文中描述了该步骤应该执行的操作:复制缓冲区的内容。可以通过多种方式完成,但是:

yG

从这里拉到最后一行,然后是:

G

将光标放在最后一行,似乎更适合这种情况。所以整个序列实际上应该是:

1GyGGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d<ENTER>

在 Vim 和 vi 中有效。

--- 编辑 ---

请注意,仍有 一些 改进空间。以下序列具有相同的结果,但没有不必要的光标移动:

:$ka|r!grep foo %<ENTER>:1,'ag/foo/d<ENTER>

它也适用于 ex ;-)。

另一个优化版本与答案试图展示的相反(文件顶部作为草稿区域而不是文件底部)但结果完全相同:

:0r!grep -v foo %<ENTER>:+,$v/foo/d<ENTER>
  • 没有正常模式光标移动,
  • 没有标记。

但是,老实说,这个问题并不需要这么复杂的方法,因为它可以用一个简单的方法来完成:

:g/foo/t$