如何颠倒线的顺序?


24

如何反转行的顺序,使第一行显示在末尾,最后一行显示在前?(可能是缓冲区中的所有行,地址范围或逐行可视模式选择。)

我想改变

rat
ox
tiger
⋮
dog
pig

进入

pig
dog
⋮
tiger
ox
rat

无需借助外部命令如tac


对这个问题有更好标签的建议吗?
200_success,2015年

1
也许是新的“ pure-vi”或类似标签?我已经看到几个可以从标记中受益的问题,这些标记表明不希望使用任何外部工具。我应该在Meta上问这个吗?
John O'M。

1
@Carpetsmoker(以及其他对此感兴趣的人)的标签问题现在位于metameta.vi.stackexchange.com/questions/1229/…–
约翰·奥姆

Answers:


29

全球力量将在这里发挥作用:

:g/^/exe "normal ddggP"

或者,更简单地说(感谢@tommcdo)

:g/^/move 0

第一行将匹配每一行,并针对每一行将其删除并将其粘贴到文件的顶部。在文件中移动时,它会反转文本。

第二个类似地匹配每一行并将其移到文件顶部。

注意:这两种方法都适用于整个文件,并且不适用于反转行的子集。请参阅Ingo Karkat的答案以获取适用于一定范围的解决方案。

描述:

g全局命令
/^/匹配任何以开头开头的行(即所有行)
exe执行以下字符串
"normal执行普通模式命令
dd删除行
gg移动到
P当前位置上方文件粘贴的顶部

move 0 将当前行移至第0行(将其置于位置1或文件的第一行)


6
代替:normal命令,我们可以使用Ex命令:move 0,它将行移动到缓冲区的开头。
tommcdo

1
此外:execute,当命令需要动态构建,如只需要:execute 'normal' g:user_command
tommcdo

@tommcdo好点!我习惯于使用,:execute因为我经常会在现有的Ex命令之后追加其他Ex命令,并且相对于:exe以后再返回并插入它,对我来说更方便。不幸的是,这个习惯渗入了这个答案中,但并没有太大用处。
John O'M。

1
关于我的用法的更多说明:execute:由于它使用字符串,因此它为普通模式命令的结束位置提供了清晰的描述,即使我没有构造字符串,与查找相比,查找平衡的引号也更容易<esc>或终止该模式的任何方法。同样,这是个人喜好和习惯。:-)
John O'M。

3
这将适用于范围btw::9,11g/^/move 8... ...最后一个数字必须是范围的开始减去1(根据Ingo的答案改编)。
马丁·图尔诺伊

13

这行代码(为您~/.vimrc定义)定义了一条:Reverse命令;您也可以:global直接使用该零件,但是(的语法:move(将行反复迭代到范围的开始之前,从而将其反转)的语法)很难记住:

:command! -bar -range=% Reverse <line1>,<line2>global/^/m<line1>-1

1
作为读者的仅供参考,<line1><line2>必须在一定范围内进行此项操作,即:(:7,9Reverse它们是command,不是globalor的特征move)。更简单的方法:command! -bar -range=% Reverse :global/^/m 0也可以使用,但仅适用于整个缓冲区...
Martin Tournoij 2015年

6

纯Vim:

:g/^/m0

说明:

根据:help multi-repeat:g它的表亲:v以两次通过的方式工作。

:g标记的第一遍每行都匹配{pattern},而第二遍(显然是从文件的开头开始并进行到结束)执行[cmd]。上面的用法:g利用了处理行的顺序(虽然没有技术上的保证,但可能还可以)。

它的工作方式是首先标记每一行,然后将第一条标记的线移动到文件的顶部,然后将第二条标记的线移动到文件的顶部(在先前移动的行上方),然后是第三条标记的行(同样在先前移动的行上方)行),以此类推,直到文件的最后一行移到顶部,从而有效地反转了文件。

请注意,如果:g以从上到下的任何顺序处理行,则此命令将不起作用。

资料来源:在vim Wikia上反转g的所有行

很少有使用外部命令的示例:

  • tacGNU coreutils的一部分- cat反向):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail 在BSD / OSX(不符合POSIX)上:

    :%!tail -r
    

    -r -r选项使输入以相反的顺序逐行显示。

    检查:man tar有关更多详细信息。

有关更多想法,请参见:


2
和约翰的答案不:g/^/m0一样:g/^/move 0吗?
muru 2015年

@muru我认为是这样,但是这个简短一些(根据vim wikia),我添加了一些不同的解释,并提供了一些其他使用命令行的示例。
kenorb 2015年

是的,由于其他命令,我投票赞成(我也来发帖tac)。但是我怀疑投票否定是因为重复了答案。
muru 2015年

我知道tacOP提到了这一点,但是所有其他类似的问题无论如何都会重复出现,因此再次提到它是很好的。John从@tommcdo评论中获取了此cmd,我最初是从DerMike那里获取的,但我认为他只是从wikia中获取的,因此我对vim wikia表示了感谢,因此,由于解释是完全不同的,因此并不完全相同。
kenorb 2015年

它增加了更多的价值,因为它的版本短得多,带有适当的解释,我也相信正确的消息来源。使用shell命令非常简单方便。如果人们不同意,他们可以简单地投票,没什么大不了的。
kenorb 2015年

6

本着功能性VimL的精神:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))返回缓冲区中所有行的列表。 '$'是一个特殊参数,line()用于指示缓冲区中的最后一行。
  • reverse(...)就地反转输入列表。reverse(copy(...))如果不应该修改输入列表,则需要使用。
  • setline(1, ...)用第二个参数替换指定的行。当第二个自变量是列表时,将与列表长度相同的行数替换为列表的内容。

如果愿意,还可以定义一个接受范围的命令(默认%整个缓冲区)

:command! -bar -range=% Reverse call setline(<line1>, reverse(getline(<line1>, <line2>)))

1
我喜欢这个答案。它也不会hlsearch:g/其他答案中的命令那样突出显示内容(如果已启用)...但是性能可能会更差吗?因为它getline(1, line('$'))获取了整个缓冲区在内存中。reverse()似乎已经就位,因此应该只占用很少的内存...
Martin Tournoij 2015年

3

根据Vim文档usr_12.txt-聪明的技巧

12.4反向行顺序

:global命令可以与:move命令结合使用,以将所有行移动到第一行之前,从而得到一个反向文件。该命令是:

:global/^/m 0

缩写:

:g/^/m 0

^正则表达式的行(即使该行是空白)的开头匹配。该:move命令将匹配的行移动到神话中的第零行之后,因此当前的匹配行成为文件的第一行。由于该:global命令不会被更改的行号所困扰,因此请:global继续以匹配文件的所有其余行,并将每行都放在第一行。

这也适用于许多行。首先移至第一行上方并用标记mt。然后将光标移到范围的最后一行并键入:

:'t+1,.g/^/m 't

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.