简体   繁体   English

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

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

I would like to delete every other line from a Vim buffer, starting with the second one, ie, lines 2, 4, 6, etc. For example, if the buffer's contents is:我想从 Vim 缓冲区中删除每隔一行,从第二个开始,即第 2、4、6 行等。例如,如果缓冲区的内容是:

aaa
bbb
ccc
ddd
eee
fff

then, after the desired line removal, it should become:然后,在删除所需的行之后,它应该变成:

aaa
ccc
eee

Which Vim commands can be used to automate this operation?哪些 Vim 命令可用于自动执行此操作?

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

:g/^/+d

You can use a macro for this.您可以为此使用宏。 Do the following.执行以下操作。

  • Start in command mode.在命令模式下启动。
  • Go to the beginning of the file by pressing gg .gg转到文件的开头。
  • Press qq .qq
  • Click arrow down and press dd after.单击向下箭头,然后按dd
  • Press q .q
  • Press 10000@q10000@q

PS: To go to command mode just press Escape a couple of times. PS:要进入命令模式,只需按 Escape 几次。

We can use the :normal (or :norm ) command to execute the j and dd Normal-mode commands:我们可以使用:normal (或:norm )命令来执行jdd正常模式命令:

:%norm jdd

Source: the Best of Vim Tips page by zzapper.来源: zzapper 的最佳 Vim 提示页面。

:map ^o ddj^o
^o

Here ^ stand for CTRL.这里 ^ 代表 CTRL。 Recursive macro to delete a line every two line.递归宏每两行删除一行。 Choose well your first line and it's done.选择好你的第一行就完成了。

from vim mail archive :来自vim 邮件存档

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

(To be typed on one line on the vim command line, will delete row 1,3,5,7,...) (要在 vim 命令行的一行中输入,将删除第 1、3、5、7 行,...)

You can always pipe though a shell command, which means you can use any scripting language you like:您始终可以通过 shell 命令进行管道传输,这意味着您可以使用任何您喜欢的脚本语言:

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

(or use "unless" instead of "if", depending on which lines you want) (或使用“除非”而不是“如果”,具体取决于您想要的行)

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

alternatively或者

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

depending on which half you want to weed out.取决于你想淘汰哪一半。

You can use Vim's own search and substitute capabilities like so: Put your cursor at the first line, and type in normal mode:您可以像这样使用 Vim 自己的搜索和替换功能:将光标放在第一行,然后在正常模式下输入:

:.,/fff/s/\n.*\(\n.*\)/\1/g
  • The .,/fff/ is the range for the substitution. .,/fff/是替换的范围。 It means "from this line to the line that matches the regex fff (in this case, the last line).它的意思是“从这一行到与正则表达式fff匹配的行(在本例中为最后一行)。
  • s/// is the substitute command. s///是替代命令。 It looks for a regex and replaces every occurrence of it with a string.它查找正则表达式并用字符串替换它的每次出现。 The g at the end means to repeat the substitution as long as the regex keeps being found.末尾的g表示只要不断找到正则表达式就重复替换。
  • The regex \\n.*\\(\\n.*\\) matches a newline, then a whole line ( .* matches any number of characters except for a newline), then another newline and another line.正则表达式\\n.*\\(\\n.*\\)匹配一个换行符,然后是整行( .*匹配除换行符之外的任意数量的字符),然后是另一个换行符和另一行。 The parentheses \\( and \\) cause the expression inside them to be recorded, so we can use it later using \\1 .括号\\(\\)会记录其中的表达式,因此我们可以稍后使用\\1使用它。
  • \\1 inserts the grouped newline and the line after it back, because we don't want the next line gone too - we just want the substitution mechanism to pass by it so we don't delete it in the next replacement. \\1插入分组的换行符和它后面的行,因为我们不希望下一行也消失 - 我们只希望替换机制通过它,所以我们不会在下一次替换中删除它。

This way you can control the range in which you want the deletion to take place, and you don't have to use any external tool.通过这种方式,您可以控制要进行删除的范围,而不必使用任何外部工具。

As another approach you could also use python if your vim has support for it.作为另一种方法,如果您的 vim 支持它,您也可以使用 python。

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

b = cb[:] temporarily copies all lines in the current buffer to b . b = cb[:]临时将当前缓冲区中的所有行复制到b b[::2] gets every second line from the buffer and assigns it to the whole current buffer cb[:] . b[::2]从缓冲区中获取每一行,并将其分配给整个当前缓冲区cb[:] The copy to b is necessary since buffer objects don't seem to support extended slice syntax.复制到b是必要的,因为缓冲区对象似乎不支持扩展切片语法。

This is probably not the "vim way", but could be easier to remember if you know python.这可能不是“vim 方式”,但如果您了解 python,可能会更容易记住。

To delete odd lines (1, 3, 5, …):删除奇数行 (1, 3, 5, ...):

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

To delete even lines (2, 4, 6, …):删除偶数行 (2, 4, 6, ...):

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

Search for text (forms the first line) followed by a new line character and some more text (forms the second line) followed by another new line character and replace the above with either first match (odd line) or second match (even line) followed by carriage return.搜索文本(形成第一行)后跟一个换行符和一些更多的文本(形成第二行)后跟另一个换行符,并用第一个匹配(奇数行)或第二个匹配(偶数行)替换上面的然后是回车。

solution解决方案

you can try this in vim你可以在 vim 中试试这个

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

edit编辑

I'll try to be more clear (two parts)我会尽量说得更清楚(两部分)

first part the selection range ( :from,to )第一部分选择范围( :from,to
second part the action ( d delete)第二部分动作( d删除)

  • the range if from first line to the last but one line ( :1,$-1 )范围 if 从第一行到最后一行( :1,$-1
  • globaly ( g ) delete ( +1d ) next line globaly ( g ) 删除 ( +1d ) 下一行

you can try to move to the first line of your file and execute :+1d you will see the next line disappear您可以尝试移至文件的第一行并执行:+1d您将看到下一行消失

I know it is muddy, but it's vim and it works我知道它是泥泞的,但它是 vim 并且它有效

Invoke sed:调用 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

Responses to comments on another answer on this question (I can't comment because stack overflow requires reputation, it's so bad)对这个问题的另一个答案的评论的回应(我无法发表评论,因为堆栈溢出需要声誉,这太糟糕了)

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

We can use the :normal (or :norm ) command to execute the j and dd Normal-mode commands:我们可以使用:normal (或:norm )命令来执行jdd正常模式命令:

 :%norm jdd

Source: the Best of Vim Tips page by zzapper.资料来源: zzapper 提供的 Vim 提示页面的精华。


This answer doesn't get enough attention, possibly because of the lack of explanation both here and on the linked, messy, cheat sheet.这个答案没有得到足够的关注,可能是因为这里和链接的、凌乱的备忘单上都没有解释。 the norm command runs the normal mode commands equivalent to the letters that follow it. norm 命令运行与它后面的字母等效的正常模式命令。 So here it is running j to move down a line, and dd to delete it.所以这里运行 j 向下移动一行, dd 删除它。 This is generalizable to the Xth line by adding more 'j's in this case.在这种情况下,通过添加更多的 'j',这可以推广到第 X 行。 – @imoatama May 13, 2014 – @imoatama 2014 年 5 月 13 日

This doesn't work (try it), but you can delete everything but the Xth line by adding more dd s (or 2dd , replacing 2 with X-1.) Or using the top answer with g , :g/^/+d2 (again replacing 2 with X-1.) Or just use a macro or sed, which also can do the original goal of deleting every Xth line for X>2.这不起作用(尝试一下),但是您可以通过添加更多dd s(或2dd ,将 2 替换为 X-1)来删除第 X 行之外的所有内容。或者使用g的最佳答案, :g/^/+d2 (再次将 2 替换为 X-1。)或者只使用宏或 sed,这也可以实现删除 X>2 的每 X 行的原始目标。

As @Lambart said,正如@Lambat 所说,

the % is shorthand for 1,$ (execute the following command from line 1 to the end) – Lambart Jul 29, 2014 % 是 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 odd line except the first line, not every third line. 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奇数行除了第一行,不是每隔三行。


I'm interested in why %norm ddj doesn't work if you want to delete odd rows.如果您想删除奇数行,我对为什么 %norm ddj 不起作用感兴趣。 – @Cyker Aug 19, 2018 ——@Cyker 2018 年 8 月 19 日

This is confusing.这令人困惑。 My guess, based on lots of testing, is that the original %norm jdd works because when something in the command hits a failure, then it stops: if you're at the end and you do jdd , it does j and then stops because it can't go past the end.根据大量测试,我的猜测是原始%norm jdd有效,因为当命令中的某些内容发生故障时,它会停止:如果您在最后并且执行jdd ,它会执行j然后停止,因为它不能 go 结束。 However, if you do ddj , it deletes first and it stops.但是,如果您执行ddj ,它将首先删除并停止。 But this doesn't stop the %norm , it only stops each line within the %norm .但这不会停止%norm ,它只会停止%norm内的每一行。

Also, the counter goes from 1 to the original line number, it doesn't update it.此外,计数器从 1 变为原始行号,它不会更新它。 (This makes sense because if it updated, you could do :%norm p and infinite loop.) And if the counter is past the last line of the current file, it goes to the last line. (这是有道理的,因为如果它更新,您可以执行:%norm p和无限循环。)如果计数器超过当前文件的最后一行,它会转到最后一行。 So it deletes all of the lines.所以它删除了所有的行。

Therefore, to delete every odd line instead of even, you can do %norm jkddj .因此,要删除每个奇数行而不是偶数行,您可以执行%norm jkddj If it's at the end, it does nothing because j is at the beginning, but if not, it deletes the current line number.如果它在末尾,它什么也不做,因为j在开头,但如果不是,它删除当前行号。 But it's easier to either just delete the first line: dd before running the command, or prepend d ( :d|%norm jdd ).但是更容易在运行命令之前删除第一行: dd ,或者在前面添加 d ( :d|%norm jdd )。 It's the same number of characters, but it's more intuitive.它的字符数相同,但更直观。

(btw this is tested on Neovim, it's probably the same as Vim but idk I didn't try) (顺便说一句,这是在 Neovim 上测试的,它可能与 Vim 相同,但我没有尝试过)

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

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