繁体   English   中英

如何删除 Vim 中的每隔一行?

[英]How to delete every other line in Vim?

我想从 Vim 缓冲区中删除每隔一行,从第二个开始,即第 2、4、6 行等。例如,如果缓冲区的内容是:

aaa
bbb
ccc
ddd
eee
fff

然后,在删除所需的行之后,它应该变成:

aaa
ccc
eee

哪些 Vim 命令可用于自动执行此操作?

完成该任务的一种优雅(且有效)的方法是调用:delete命令(参见:help :d )为+行范围(与.+1相同)寻址当前行之后的行(参见:help {address} ),在每一行(参见:help /^ )使用:global命令(参见:help :g ):

:g/^/+d

您可以为此使用宏。 执行以下操作。

  • 在命令模式下启动。
  • gg转到文件的开头。
  • qq
  • 单击向下箭头,然后按dd
  • q
  • 10000@q

PS:要进入命令模式,只需按 Escape 几次。

我们可以使用:normal (或:norm )命令来执行jdd正常模式命令:

:%norm jdd

来源: zzapper 的最佳 Vim 提示页面。

:map ^o ddj^o
^o

这里 ^ 代表 CTRL。 递归宏每两行删除一行。 选择好你的第一行就完成了。

来自vim 邮件存档

:let i=1 | while i <= line('$') | if (i % 2) | exe i . "delete" | endif | let i += 1 | endwhile

(要在 vim 命令行的一行中输入,将删除第 1、3、5、7 行,...)

您始终可以通过 shell 命令进行管道传输,这意味着您可以使用任何您喜欢的脚本语言:

:%!perl -nle 'print if $. % 2'

(或使用“除非”而不是“如果”,具体取决于您想要的行)

:%!awk -- '++c\%2'

或者

:%!awk -- 'c++\%2'

取决于你想淘汰哪一半。

您可以像这样使用 Vim 自己的搜索和替换功能:将光标放在第一行,然后在正常模式下输入:

:.,/fff/s/\n.*\(\n.*\)/\1/g
  • .,/fff/是替换的范围。 它的意思是“从这一行到与正则表达式fff匹配的行(在本例中为最后一行)。
  • s///是替代命令。 它查找正则表达式并用字符串替换它的每次出现。 末尾的g表示只要不断找到正则表达式就重复替换。
  • 正则表达式\\n.*\\(\\n.*\\)匹配一个换行符,然后是整行( .*匹配除换行符之外的任意数量的字符),然后是另一个换行符和另一行。 括号\\(\\)会记录其中的表达式,因此我们可以稍后使用\\1使用它。
  • \\1插入分组的换行符和它后面的行,因为我们不希望下一行也消失 - 我们只希望替换机制通过它,所以我们不会在下一次替换中删除它。

通过这种方式,您可以控制要进行删除的范围,而不必使用任何外部工具。

作为另一种方法,如果您的 vim 支持它,您也可以使用 python。

:py import vim; cb = vim.current.buffer; b = cb[:]; cb[:] = b[::2]

b = cb[:]临时将当前缓冲区中的所有行复制到b b[::2]从缓冲区中获取每一行,并将其分配给整个当前缓冲区cb[:] 复制到b是必要的,因为缓冲区对象似乎不支持扩展切片语法。

这可能不是“vim 方式”,但如果您了解 python,可能会更容易记住。

删除奇数行 (1, 3, 5, ...):

:%s/\(.*\)\n\(.*\)\n/\2\r/g

删除偶数行 (2, 4, 6, ...):

:%s/\(.*\)\n.*\n/\1\r/g

搜索文本(形成第一行)后跟一个换行符和一些更多的文本(形成第二行)后跟另一个换行符,并用第一个匹配(奇数行)或第二个匹配(偶数行)替换上面的然后是回车。

解决方案

你可以在 vim 中试试这个

:1,$-1g/^/+1d

编辑

我会尽量说得更清楚(两部分)

第一部分选择范围( :from,to
第二部分动作( d删除)

  • 范围 if 从第一行到最后一行( :1,$-1
  • globaly ( g ) 删除 ( +1d ) 下一行

您可以尝试移至文件的第一行并执行:+1d您将看到下一行消失

我知道它是泥泞的,但它是 vim 并且它有效

调用 sed:

:% !sed -e '2~2 d'

^^^^                  pipe file through a shell command
    ^^^^^^            the command is sed, and -e describes an expression as parameter
            ^^^^^     starting with the second line, delete every second line

对这个问题的另一个答案的评论的回应(我无法发表评论,因为堆栈溢出需要声誉,这太糟糕了)

https://stackoverflow.com/a/8334936/16603562

我们可以使用:normal (或:norm )命令来执行jdd正常模式命令:

 :%norm jdd

资料来源: zzapper 提供的 Vim 提示页面的精华。


这个答案没有得到足够的关注,可能是因为这里和链接的、凌乱的备忘单上都没有解释。 norm 命令运行与它后面的字母等效的正常模式命令。 所以这里运行 j 向下移动一行, dd 删除它。 在这种情况下,通过添加更多的 'j',这可以推广到第 X 行。 – @imoatama 2014 年 5 月 13 日

这不起作用(尝试一下),但是您可以通过添加更多dd s(或2dd ,将 2 替换为 X-1)来删除第 X 行之外的所有内容。或者使用g的最佳答案, :g/^/+d2 (再次将 2 替换为 X-1。)或者只使用宏或 sed,这也可以实现删除 X>2 的每 X 行的原始目标。

正如@Lambat 所说,

% 是 1,$ 的简写(从第 1 行到最后执行以下命令)– Lambart 2014 年 7 月 29 日

So if you do %norm jjdd , it will start the counter at line 1, then go to line 3 and delete it, but then it will go to line 2 and then go to line 4 and delete it, etc. This results deleting every奇数行除了第一行,不是每隔三行。


如果您想删除奇数行,我对为什么 %norm ddj 不起作用感兴趣。 ——@Cyker 2018 年 8 月 19 日

这令人困惑。 根据大量测试,我的猜测是原始%norm jdd有效,因为当命令中的某些内容发生故障时,它会停止:如果您在最后并且执行jdd ,它会执行j然后停止,因为它不能 go 结束。 但是,如果您执行ddj ,它将首先删除并停止。 但这不会停止%norm ,它只会停止%norm内的每一行。

此外,计数器从 1 变为原始行号,它不会更新它。 (这是有道理的,因为如果它更新,您可以执行:%norm p和无限循环。)如果计数器超过当前文件的最后一行,它会转到最后一行。 所以它删除了所有的行。

因此,要删除每个奇数行而不是偶数行,您可以执行%norm jkddj 如果它在末尾,它什么也不做,因为j在开头,但如果不是,它删除当前行号。 但是更容易在运行命令之前删除第一行: dd ,或者在前面添加 d ( :d|%norm jdd )。 它的字符数相同,但更直观。

(顺便说一句,这是在 Neovim 上测试的,它可能与 Vim 相同,但我没有尝试过)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM