我们可以使用 m 来移动线条,使用 j 来连接线条。例如,如果您有一个列表,并且想要分离所有匹配的内容(或者相反,不匹配某些模式)而不删除它们,那么您可以使用类似: :% g/foo/m$ ... 以及所有“ foo”行将被移至文件末尾。(请注意有关使用文件末尾作为暂存空间的另一个提示)。这将保留所有“foo”行的相对顺序,同时从列表的其余部分中提取它们。(这相当于执行以下操作:(
1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d将文件复制到其自己的尾部,通过 grep 过滤尾部,然后删除头部的所有内容)。
浏览了吉姆·丹尼斯的传奇答案,但我仍然无法理解这个序列:
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
Run Code Online (Sandbox Code Playgroud)
1G将光标移动到第 1 行,参见:help G,!GG 没有意义,ma创建标记a,请参阅:help m,p将未命名寄存器的内容放在光标后面,请参阅:help p,!Ggrep foo<ENTER>使用外部命令过滤从当前行到最后一行的行grep foo,请参阅:help !,1G将光标移动到第 1 行,:1,'a g/foo'/dfoo'剪切从第 1 行到标记 的所有匹配行a,请参阅:help :range、:help :g和:help d。我认为第二步是一个错字。!GG字面意思是“使用外部命令过滤从当前行到最后一行的文本G”,这是没有意义的,因为 a)G不是一个常见的 Unix 命令(如果它存在的话),并且 b) 它后面没有<ENTER>,这是执行 Ex 命令所需的。
答案的正文中描述了该步骤应该执行的操作:复制缓冲区的内容。可以通过多种方式完成,但是:
yG
Run Code Online (Sandbox Code Playgroud)
从这里拉到最后一行,然后是:
G
Run Code Online (Sandbox Code Playgroud)
将光标放在最后一行,在这种情况下似乎更合适。所以整个序列实际上应该是:
1GyGGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d<ENTER>
Run Code Online (Sandbox Code Playgroud)
它可以在 Vim 和 vi 中运行。
- - 编辑 - -
请注意,仍有一些改进的空间。以下序列具有相同的结果,但没有不必要的光标移动:
:$ka|r!grep foo %<ENTER>:1,'ag/foo/d<ENTER>
Run Code Online (Sandbox Code Playgroud)
它也适用于前;-)。
另一个优化版本的做法与答案试图演示的相反(文件顶部作为暂存区域而不是文件底部),但结果完全相同:
:0r!grep -v foo %<ENTER>:+,$v/foo/d<ENTER>
Run Code Online (Sandbox Code Playgroud)
但是,说实话,这个问题并不需要如此复杂的解决方案,因为它可以通过简单的方法来完成:
:g/foo/m$
Run Code Online (Sandbox Code Playgroud)
它适用于 ex、vi、每个 vi 克隆,甚至 ed。