在Vim中合并多行(两个块)

Thi*_*ter 323 vim

我想在Vim中合并两个线块,即取线n..m并将它们附加到线条上a..b.如果您更喜欢伪代码解释:[a[i] + b[i] for i in min(len(a), len(b))]

例:

abc
def
...

123
45
...
Run Code Online (Sandbox Code Playgroud)

应该成为

abc123
def45
Run Code Online (Sandbox Code Playgroud)

没有手动复制和粘贴,有没有一种很好的方法可以做到这一点?

ram*_*ion 880

你当然可以通过一次复制/粘贴(使用块模式选择)完成所有这些,但我猜这不是你想要的.

如果您只想使用Ex命令执行此操作

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

会改变

work it 
make it 
do it 
makes us 
harder
better
faster
stronger
~
Run Code Online (Sandbox Code Playgroud)

work it harder
make it better
do it faster
makes us stronger
~
Run Code Online (Sandbox Code Playgroud)

更新:这个许多赞成票的答案值得更彻底的解释.

在Vim中,您可以使用管道字符(|)来链接多个Ex命令,因此上述内容相当于

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

许多Ex命令接受一系列行作为前缀参数 - 在上面的例子中,5,8在指定命令操作的行之前del1,4之前s///.

del删除给定的行.它可以采用一个寄存器参数,但是如果没有给出,它会将这些行转储到未命名的寄存器,@"就像在正常模式下删除一样. let l=split(@")然后使用默认分隔符:空格将已删除的行拆分为列表.要在已删除行中包含空格的输入上正常工作,例如:

more than 
hour 
our 
never 
ever
after
work is
over
~
Run Code Online (Sandbox Code Playgroud)

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

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

  • 确保像你这样的人想要发布这样的答案,这就是我成为主持人的原因.不错的表演 :) (16认同)
  • 如果你想要完整的vim参考答案:`:help range`,`:help:d`,`:help:let`,`:help split()`,`:help:s`,`:help:s\=`,`:help remove()`. (11认同)

ib.*_*ib. 58

优雅而简洁的Ex命令解决的问题可以通过组合获得:global,:move:join命令.假设第一行的行在缓冲区的第一行开始,并且光标位于紧接第二块的第一行之前的行上,则命令如下.

:1,g/^/''+m.|-j!
Run Code Online (Sandbox Code Playgroud)

对于所使用的技术的详细说明,请参阅答案我给了这个问题:" Vim的粘贴-d'行为开箱? ".

  • 非常好!我不知道`:move`和`:join!`,也不知道''''作为范围参数(`:help''`)和什么`+`和`-`意味着范围修饰符( `:帮助范围`).谢谢! (3认同)
  • @ib.:我认为将详细解释也纳入这个答案是个好主意. (2认同)

mli*_*elt 44

要加入行块,您必须执行以下步骤:

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

运动不是最好的(我不是专家),但它的工作方式与你想要的一样.希望会有更短的版本.

以下是先决条件,因此这项技术运作良好:

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

  • 这只是因为`abc`和`def`是相同的长度.块选择将保留已删除文本的缩进,因此如果光标在放置文本时位于短行上,则行将插入较长文本上的字母之间 - 如果光标位于较长的字母上,则将空格附加到较短的字母之间一. (13认同)
  • 就像@Izkata所说,你会遇到在较长的行之间插入文本的问题.要解决此问题,只需在第一行末尾添加更多空格以使其最长,然后粘贴您的文本块.一旦完成,将多个空格压缩为一个就像`:%s /\+// g`一样简单 (2认同)

Ken*_*ger 19

这是我怎么做的(光标在第一行):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a
Run Code Online (Sandbox Code Playgroud)

你需要知道两件事:

  • 第二组第一行开始的行号(在我的情况下为5),和
  • 每组中的行数(在我的示例中为3).

这是发生了什么:

  • qa将所有内容记录到下q一个"缓冲区"中a.
  • ma 在当前行上创建一个标记.
  • :5<CR> 去下一组.
  • y$ 猛拉其余部分.
  • 'a 返回到先前设置的标记.
  • $p 贴在行尾.
  • :5<CR> 返回第二组的第一行.
  • dd 删除它.
  • 'a 回到标记.
  • jq 下降一行,并停止录音.
  • 3@a 重复每一行的动作(在我的情况下为3)


kev*_*ler 8

如其他地方所述,块选择是要走的路.但您也可以使用以下任何变体:

:!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.
Run Code Online (Sandbox Code Playgroud)

  • 除此之外,我在Windows上,所以解决方案将涉及打开到我的Linux机器的SSH连接,并从编辑器粘贴到终端并返回. (3认同)