考虑:
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/