在VIM中的ex命令中重复使用之前的范围

9

在VIM中,是否可以重复使用ex命令的范围?

举个例子,我可以使用以下命令将当前文件中的第4行到第10行复制到一个新文件中:

:4,10w foo/bar.txt

但实际上我想要做的是将这些行移动到一个新文件中。可以这样做:

:4,10w foo/bar.txt
:4,10d

但是每次都要输入4,10有点烦人。

所以我有两个问题:

  1. 一般来说,是否有一种方法在ex命令中引用先前使用的范围?
  2. 具体地,如果没有方法执行(1),是否有更简单的方法将多行从一个文件中剪切并粘贴到新文件中。

请查看此答案:https://stackoverflow.com/a/34481997/311618 - zahir
8个回答

5
我通常使用cat来完成这个任务:
:4,10!cat > foo/bar.txt

这是可行的,因为你通过cat命令管道传输文本并用其输出替换它们,即没有实际输出内容。当然,如果你要将文本追加到现有文件末尾,可以使用>>代替>。


这是一种有趣的处理方式!有点优雅。 - steveha

4

我不知道如何回答(1),但是对于(2)的问题,有很多不需要手动重新选择行的方法。在可视化模式下,可以使用以下方法:

4GV10G
:w foo/bar.txt
gvd

因为gv重新选择了先前的选择,这几乎是您想要的,而不需要使用ex范围。但是,您可以从反面考虑问题,尝试:
:4,10d
:sp foo/bar.txt
pZZ

剪切,然后粘贴到新文件中,最后关闭它。

3
除了使用Vim历史记录(:光标上移q:)并删除前一个命令以保留范围之外,没有其他方法可以重复使用最后一个范围,也没有魔术变量。
如果我经常使用这个“移动行”组合,我会为它编写一个自定义命令:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _

您只需要指定一次范围,就可以省去输入。

您也可以为其他组合写出类似的语句。主要的挑战是指定所有的命令属性(bang、范围、完成),以及后来记住自定义命令名称。


2

通常我所做的是删除一个文件中的行,转到另一个文件,然后粘贴。

此外,我通常使用标记。而不是输入实际数字,我按下 mb 标记开始行,然后去到结束行并按下 d'b 删除到被标记为 b 的行。但您也可以使用 mb 标记开始行和 me 标记结束行,然后运行 ex 命令:

:'b,'e w somefile.txt<Enter>

当然,您可以使用任意从 az 的字母作为标记;我通常使用 be,但您可以使用您喜欢的字母。

我如何移动这些行:

m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^

按下Ctrl+^可以从当前打开的文件切换到上一个打开的文件。(如果您喜欢,您也可以只打开一个窗格并切换窗格。在普通的vi中不支持窗格,但在vim中是可以的)

上述假设您已经设置了autowrite选项。通过autowrite:n命令和Ctrl+^都只会先写入当前文件,然后再切换到其他文件,而不是抱怨文件已被更改而未保存。您还可以在使用:n或Ctrl+^之前明确地写入文件。

顺便说一句,我使用Ctrl+^非常频繁,所以我将其映射到了K键。这样更容易输入,但我很久以前养成了这个习惯,当时我有时必须使用无法输入Ctrl+^的哑终端。

顺便说一下,当您删除行时,它们会进入“未命名缓冲区”。在vim中,当您切换文件时,未命名的缓冲区会被保留。在原始的vi中,未命名的缓冲区会被清除。因此,上述方法对于旧版本的vi不起作用。您可以通过删除到命名缓冲区,然后从命名缓冲区粘贴来使其在任何版本的vi中都可以使用。

m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^

上述操作将内容删除到名为 a 的缓冲区,然后从 a 中粘贴到另一个文件中。当然,在vim中也能做到这一点,只是你无需这样做。

2
这里有一个命令行映射可以实现这个功能。我将它绑定到CTRL-G CTRL-U,因为它执行的动作与CTRL-U类似。(当然你可以更改它!)
" c_CTRL-G_CTRL-U   Remove all characters between the cursor position and
"           the closest previous |:range| given to a command. When
"           directly after a range, remove it.
"           Useful to repeat a recalled command line with the same
"           range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\@<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
    let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
    let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)

    let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
    if empty(l:upToRange)
        return getcmdline()
    else
        call setcmdpos(strlen(l:upToRange) + 1)
        return l:upToRange . l:cmdlineAfterCursor
    endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>

作为插件

我的CmdlineSpecialEdits插件中包含了许多其他的映射。


1

您也可以像这样将匿名寄存器的内容写入file2.txt文件中

 :4,10d | :call writefile(split(@@, "\n", 1), 'file2.txt')

0

你可以先删除,然后打开一个新标签页并粘贴内容 - 所以:4,10d,然后:tabe foo/bar.txt,最后p... 这样听起来更好吗?


0

在编写本文时,Vim 8和NVIM 0.3.7中,您可以实际编辑命令列表并按回车键执行。

:4,10w foo/bar.txt
q:

q: 是进入交互式 ex 命令

一旦打开交互式命令列表,您可以编辑它并按回车键执行。

我喜欢 moopet's answer,它很高效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接