为什么使用 g/re/p 替换换行符只适用于 Vim 中的所有其他行?

gvk*_*vkv 4 vim

考虑:

foo
bar
....
bar
baz
Run Code Online (Sandbox Code Playgroud)

然后:

:g/bar/s/\n/\r\r/g
Run Code Online (Sandbox Code Playgroud)

意外返回:

foo
bar

bar
bar

bar
bar

...
baz
Run Code Online (Sandbox Code Playgroud)

当我在每个bar. 然而:

:g/bar/p
Run Code Online (Sandbox Code Playgroud)

返回:

bar
bar
...
bar
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,如果所有bars之间都有换行符,我就会得到我所期望的——每行包含bar. 我通过以下方式解决了这个问题:

:%s/\(bar.*\)\n/\1\r\r/g
Run Code Online (Sandbox Code Playgroud)

但我真的很想更好地了解正在发生的事情。

Gil*_*il' 8

要了解在这个细节级别上发生了什么,我认为您必须获取源代码。因此,引用vim-7.1.314/src/ex_cmds.c(在 的定义开头的注释ex_global()):

这是通过两遍实现的:首先我们扫描文件中的模式,并为(不)匹配的每一行设置一个标记。其次,我们为每一行有一个标记执行命令。这是必需的,因为删除行后我们不知道在哪里搜索下一个匹配项。

因此,如果您删除:g'ed 命令中的一行,该行的标记也会消失。如果在:g'ed 命令中创建一行,它不会被标记,因此该命令不会在其上执行。在您的示例中,第 3 行与第 2 行连接,因此第 3 行的标记消失,第 2 行之后的下一个标记是最初放在第 4 行的标记。

此外,ex_global设置global_busy变量,这会导致一些命令(包括:s)的行为略有不同。不过,我认为差异在这里无关紧要。