如何合并两个多行文本块在Vim?

我想合并Vim中的两个行块,即采取行kl,并将它们附加到行n。如果你更喜欢伪代码解释:[line[k+i] + line[m+i] for i in range(min(l-k, n-m)+1)]

例如,

abc
def
...


123
45
...

应该成为

abc123
def45

有没有一种很好的方法来做到这一点,而无需手动逐行复制和粘贴?

81031 次浏览

要连接行块,您必须执行以下步骤:

  1. 转到第三行:jj
  2. 进入可视块模式:CTRL-v
  3. 将光标锚定到行尾(对于不同长度的行很重要):$
  4. 进入结尾:CTRL-END
  5. 切块:x
  6. 转到第一行的末尾:kk$
  7. 将块粘贴在这里:p

这个动作不是最好的(我不是专家),但它像你想要的那样工作。希望会有一个更短的版本。

以下是前提条件,所以这个技巧可以很好地工作:

  • 起始块的所有行(在问题abcdef的示例中)具有相同的长度XOR
  • 起始块的第一行是最长的,并且你不关心中间的额外空格)XOR
  • 起始块的第一行并不是最长的,你可以在末尾添加空格。

您当然可以通过单个复制/粘贴(使用块模式选择)完成所有这些操作,但我猜这不是您想要的。

如果你想用前女友命令来做这个

:5,8del | let l=split(@") | 1,4s/$/\=remove(l,0)/

将会改变

work it
make it
do it
makes us
harder
better
faster
stronger
~

work it harder
make it better
do it faster
makes us stronger
~

一个有这么多赞的答案值得一个更彻底的解释。

在Vim中,你可以使用管道字符(|)来连接多个Ex命令,所以上面等价于

:5,8del
:let l=split(@")
:1,4s/$/\=remove(l,0)/

许多Ex命令接受一个行范围作为前缀参数——在上面的例子中,5,8del之前,1,4s///之前指定命令操作的行。

del删除给定的行。它可以接受一个寄存器参数,但是当没有给出一个参数时,它会将行转储到未命名的寄存器@"中,就像在正常模式下删除一样。let l=split(@")然后将删除的行分割成一个列表,使用默认分隔符:空白。在被删除的行中有空格的输入上正常工作,例如:

more than
hour
our
never
ever
after
work is
over
~

我们需要指定一个不同的分隔符,以防止“work is”被分割成两个列表元素:let l=split(@","\n")

最后,在替换s/$/\=remove(l,0)/中,我们用表达式remove(l,0)的值替换每行的结尾($)。remove(l,0)改变列表l,删除并返回它的第一个元素。这让我们可以按照读取它们的顺序替换被删除的行。我们可以使用remove(l,-1)来替换被删除的行。

下面是我的做法(把光标放在第一行上):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a

你需要知道两件事:

  • 第二组的第一行开始的行号(在我的例子中是5),以及
  • 每组中的行数(在我的例子中是3)。

事情是这样的:

  • qa将下一个q之前的所有内容记录到a中的一个“缓冲区”中。
  • ma在当前行上创建一个标记。
  • :5<CR>到下一组。
  • y$拉起其余的行。
  • 'a返回之前设置的标记。
  • $p粘贴在行尾。
  • :5<CR>返回到第二组的第一行。
  • dd删除它。
  • 'a返回标记。
  • jq向下一行,并停止录制。
  • 3@a重复每一行的操作(在我的例子中是3)

正如在其他地方提到的,块选择是可行的方法。但是你也可以使用以下的任何变体:

:!tail -n -6 % | paste -d '\0' % - | head -n 5

此方法依赖于UNIX命令行。paste实用程序被创建来处理这种行合并。

PASTE(1)                  BSD General Commands Manual                 PASTE(1)


NAME
paste -- merge corresponding or subsequent lines of files


SYNOPSIS
paste [-s] [-d list] file ...


DESCRIPTION
The paste utility concatenates the corresponding lines of the given input files, replacing all but the last file's newline characters with a single tab character,
and writes the resulting lines to standard output.  If end-of-file is reached on an input file while other input files still contain data, the file is treated as if
it were an endless source of empty lines.

一个优雅简洁的Ex命令可以通过 组合:global:move:join命令。假设 第一个行块从缓冲区的第一行开始 光标位于第一行的前一行 第2块的行,命令如下:

:1,g/^/''+m.|-j!

有关此技术的详细说明,请参见我的答案 to 一个本质上相同的问题" 如何实现"粘贴-d '␣' " " .

. {/p> .

我将使用复杂重复:)

鉴于这种:

aaa
bbb
ccc


AAA
BBB
CCC

将光标放在第一行,按下以下命令:

qa}jdd''pkJxjq

然后按@a(随后你可能会使用@@)根据需要多次。

你应该得到:

aaaAAA
bbbBBB
cccCCC

(加上换行符。)

解释:

  • qa开始在a中记录一个复杂的重复

  • }跳转到下一个空行

  • jdd删除下一行

  • ''返回到上次跳转之前的位置

  • p将删除的行粘贴到当前行的下面

  • kJ将当前行追加到前一行的末尾

  • x删除J在合并行之间添加的空格;如果你需要空间,你可以省略这个

  • j转到下一行

  • q结束复杂的重复记录

在此之后,你可以使用@a来运行存储在a中的复杂重复,然后你可以使用@@来重新运行上次运行的复杂重复。

样本数据与rampion的相同。

:1,4s/$/\=getline(line('.')+4)/ | 5,8d
我不认为把它弄得太复杂。 我只需将virtualedit设置为
(:set virtualedit=all) < br >
.
. 把它放在第一列之后:

abc    123
def    45
...    ...

并将其中的多重空格移除为1个空格:

:%s/\s\{2,}/ /g

有很多方法可以做到这一点。我将使用以下两种方法中的任何一种来合并两个文本块。

假设第一个块在第1行,第二个块从第10行开始,游标的初始位置在第1行。

(\n表示按回车键。)

1. abc
def
ghi


10. 123
456
789

与宏使用命令:复制,粘贴和连接。

y \ npkJjq2@a10G3dd qaqqa: + 9

使用宏命令在第n行号移动一行并连接。

qcqqc: 10 m。\ nkJjq2@c