我想在 Vim 中合并两行代码,即取行 k 到 l 并将它们附加到行 m 到   ;n.如果您更喜欢伪代码解释:[line[k+i] + line[m+i] for i in range(min(l-k, n-m)+1)]
。
例如,
abc
def
...
123
45
...
应该成为
abc123
def45
有没有一种很好的方法可以做到这一点,而无需逐行手动复制和粘贴?
x+2
加入行 x
?
[a[i] + b[i] for i in min(len(a), len(b))]
您当然可以通过一次复制/粘贴(使用块模式选择)来完成所有这些,但我猜这不是您想要的。
如果您只想使用 Ex 命令执行此操作
: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 命令接受一系列行作为前缀参数 - 在上述情况下,del
之前的 5,8
和 s///
之前的 1,4
指定命令操作的行。
del
删除给定的行。它可以接受一个寄存器参数,但如果没有给出,它会将行转储到未命名的寄存器 @"
,就像在正常模式下删除一样。 let l=split(@")
然后将删除的行拆分为一个列表,使用默认分隔符:空格。要在已删除行中有空格的输入上正常工作,例如:
more than
hour
our
never
ever
after
work is
over
~
我们需要指定不同的分隔符,以防止“工作是”被拆分为两个列表元素:let l=split(@","\n")
。
最后,在替换 s/$/\=remove(l,0)/
中,我们将每一行的结尾 ($
) 替换为表达式 remove(l,0)
的值。 remove(l,0)
更改列表 l
,删除并返回其第一个元素。这让我们可以按照阅读的顺序替换已删除的行。我们可以使用 remove(l,-1)
以相反的顺序替换已删除的行。
通过组合 :global
、:move
和 :join
命令可以获得一个优雅而简洁的 Ex 命令来解决该问题。假设第一个行块从缓冲区的第一行开始,并且光标位于第二个块的第一行之前的行,命令如下。
:1,g/^/''+m.|-j!
有关此技术的详细说明,请参阅my answer 对基本相同的问题“How to achieve the “paste -d '␣'” behavior out of the box in Vim?”。
E16: Invalid Range
- 但它仍然有效。删除 1,
时,它可以正常工作而不会出现错误。
:move
和 :join!
,也不知道 ''
作为范围参数 (:help ''
) 的含义以及 +
和 -
作为范围修饰符 (:help range
) 的含义。谢谢!
sil!
来抑制错误消息。
要加入线块,您必须执行以下步骤:
转到第三行:jj 进入可视块模式:CTRL-v 将光标定位到行尾(对于不同长度的行很重要):$ 转到末尾:CTRL-END 剪切块:x 转到第一行的结尾:kk$ 在此处粘贴块:p
该运动不是最好的(我不是专家),但它可以像您想要的那样工作。希望会有一个更短的版本。
这是先决条件,因此此技术效果很好:
起始块的所有行(在问题 abc 和 def 的示例中)具有相同的长度 XOR
起始块的第一行是最长的,你不需要关心中间的额外空格)XOR
起始块的第一行不是最长的,并且您在末尾添加空格。
abc
和 def
的长度相同。块选择将保留已删除文本的缩进,因此,如果在放置文本时光标位于短行上,则行将插入较长的字母之间 - 如果光标位于较长的字母上,则将空格附加到较短的字母一。
:%s/ \+/ /g
set ve=all
应该有帮助,请参阅 vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit'
这是我的做法(光标在第一行):
qama:5<CR>y$'a$p:5<CR>dd'ajq3@a
你需要知道两件事:
第二组的第一行开始的行号(在我的情况下为 5),以及
每组中的行数(在我的示例中为 3)。
这是发生了什么:
qa 将直到下一个 q 的所有内容记录到 a 中的“缓冲区”中。
ma 在当前行上创建一个标记。
:5
y$ 抽出该行的其余部分。
'a 回到先前设定的标记。
$p 粘贴在行尾。
:5
dd 删除它。
'a 回到标记。
jq 下降一行,并停止记录。
3@a 为每一行重复该操作(在我的情况下为 3)
:5
后按 [Enter]
,否则这将不起作用。
:norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@a
,但似乎只执行了 q
。
:let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a
,其中 ^M
字符是通过按 CTRL-V 然后 Enter 来输入的。
如其他地方所述,块选择是要走的路。但您也可以使用以下任何变体:
:!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.
paste -d
的)行为可以通过简短的 Vim 命令实现,如 my answer 所示。
样本数据与 rampion 的相同。
:1,4s/$/\=getline(line('.')+4)/ | 5,8d
我不认为让它太复杂。我只需将 virtualedit 设置为
(:set virtualedit=all
)
选择块 123 及以下所有内容。
将其放在第一列之后:
abc 123
def 45
... ...
并删除到 1 个空格之间的多个空格:
:%s/\s\{2,}/ /g
gvV:'<,'>s/\s+//g
的事情(vim 应该自动为您插入 '<,'>
,这样您就不需要手动输入它)。
我会使用复杂的重复:)
鉴于这种:
aaa
bbb
ccc
AAA
BBB
CCC
将光标放在第一行,按以下命令:
qa}jdd''pkJxjq
然后按需要多次按 @a
(您可以随后使用 @@
)。
你最终应该得到:
aaaAAA
bbbBBB
cccCCC
(加上换行符。)
解释:
qa 开始在 a 中记录复杂的重复
跳转到下一个空行
jdd 删除下一行
'' 回到上次跳转前的位置
将删除的行粘贴到当前行下
kJ 将当前行附加到上一行的末尾
删除 J 在组合行之间添加的空间;如果你想要空间,你可以省略它
转到下一行
结束复杂的重复录音
之后,您将使用 @a
运行存储在 a
中的复杂重复,然后您可以使用 @@
重新运行上次运行的复杂重复。
可以有很多方法来实现这一点。我将使用以下两种方法中的任何一种来合并两个文本块。
假设第一个块在第 1 行,第二个块从第 10 行开始,光标的初始位置在第 1 行。
(\n 表示按回车键。)
1. abc
def
ghi
10. 123
456
789
使用以下命令使用宏:复制、粘贴和加入。
qaqqa:+9y\npkJjq2@a10G3dd
使用命令使用宏在第 n 行号处移动一行并加入。
qcqqc:10m .\nkJjq2@c
不定期副业成功案例分享
s/\s*$/
而不是s/$/
删除任何尾随空格。:sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohls
似乎更好,因为它在运行后会清理搜索历史。而且它没有显示要求我按回车的“x more/less lines”消息。:help range
、:help :d
、:help :let
、:help split()
、:help :s
、:help :s\=
、:help remove()
。