我认为问题不在于^M 与^J。 Vim 宏会将其中任何一个视为记录的宏的有效行尾字符。我认为问题在于额外的换行符。
在您的示例中,2j 之后至少有一个虚假的换行符,除非您在复制 sn-p 时特别小心,否则10k 之后可能还有另一个。这些额外的换行符就像在普通模式下按<Enter>——它们将光标向下移动一行。
这就是我认为您希望 sn-p 的样子:
:s/foo/bar/g
2j:s/1/2/g
10k
(即使这有点误导 - 您仍然需要注意不要在 10k 之后复制换行符。)
为什么这些额外的换行符会产生如此大的影响?好吧,一方面,它们会导致您与预期的位置相距至少一行,这会导致您想要在特定行上执行的任何操作(例如执行 :s// 命令)。
然而,更重要的是——这就是我认为在你的例子中发生的事情——如果宏试图在缓冲区的最后一行使用<Enter>,Vim 会停止宏播放。 (我猜 Vim 认为这是一个错误,任何错误都会导致宏停止运行。)
这是一个例子。假设你有这个 sn-p 存储在寄存器 x 中:
4j
:echo "Done"
(注意4j之后的换行符。)
此外,假设您在缓冲区中有以下五行(并且只有这五行):
line 1
line 2
line 3
line 4
line 5
如果您现在在line 1 上按@x,则:echo "Done" 永远不会执行。 Vim 将光标向下移动 4 行到line 5,然后由于额外的换行符而尝试再向下移动一行,但它不能。在:echo 命令有机会运行之前,该宏将停止执行。
但是,如果您将 x 寄存器更改为以下内容,它会起作用:
4j:echo "Done"
所以回到你原来的例子,我敢打赌,2j 之后的额外换行符试图将你的光标移动到它不能去的地方,这会导致宏停止。屏幕的最后一行包含最后执行的命令 (:s/foo/bar/g),这看起来像是 Vim 正在等待您按 Return。
最后,我强烈建议使用另一种方法来存储和执行 Vim 命令序列。您使用的技术对于简单的情况是可以容忍的,但它很脆弱并且不能很好地扩展。 Vim 有一个完整的脚本语言,包括函数和自定义命令,它可以用来做你现在正在做的所有事情,但是以更健壮的方式。 Vim 脚本是一个很大的话题,但我会从这里开始:
:help script
请务必阅读:normal 命令,该命令可让您在脚本中执行普通模式命令(如2j 和10k)。
祝你好运!