为什么使用 g/re/p 替换换行符仅适用于 Vim 中的每隔一行?

为什么使用 g/re/p 替换换行符仅适用于 Vim 中的每隔一行?

考虑:

foo
bar
....
bar
baz

然后:

:g/bar/s/\n/\r\r/g

意外返回:

foo
bar

bar
bar

bar
bar

...
baz

当我想在每个之后换行时bar。然而:

:g/bar/p

返回:

bar
bar
...
bar

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

:%s/\(bar.*\)\n/\1\r\r/g

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

答案1

要了解这个详细程度的情况,我认为您必须获取源代码。因此,引用vim-7.1.314/src/ex_cmds.c(定义头部的注释ex_global()):

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

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

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

答案2

吉尔斯已经回答了您的“为什么?”,但我认为您可能会对一种更简单的方法来完成您想要的事情感兴趣。

我想在每个之后换行bar

不要替换行终止字符(从而根据吉尔斯的答案连接行并丢失内部标记),而是在每个匹配行末尾之前添加一个空行。用于$在行尾之前获得零宽度匹配,并在此处插入换行符(也不需要修饰符,/g因为模式每行仅匹配一次)。

:g/bar/s/$/\r/

相关内容