Vim处理文本之奇偶行删除已经介绍了使用Vim的 :global 命令实现奇偶行删除的操作,本文介绍使用 :normal 命令来实现奇偶行删除的功能。

从Vim教程网整理的Vim normal命令和重复操作一文知道,normal 命令的使用形式为 :{range}norm[al][!] {commands},表示在 range 指定范围内的每行执行若干普通模式命令 commands。

在具体介绍使用 normal 命令完成奇偶行删除之前,需要强调下 normal 命令的内部运行机制,即:纯粹根据 range 指定的行数来逐行运行Vim普通模式下的命令 commands。

例如,在下面名为 test.txt 文本中,执行Vim命令 :normal 1,3 dd 并不是删除了1~3行的内容,而是删除了1、3、5行的内容,剩下了原始文件中的2、4、6行。

1
2
3
4
5
6
aa
bb
cc
dd
ee
ff

这是因为,normal 命令是纯粹根据行号来逐行执行的,在第一行执行完 dd 命令删除第一行后,原文件的第二行变成了第一行,接着,normal 命令将在文件的第二行继续执行 dd 命令,此时通过 dd 命令删除的第二行实则是原始文件的第三行。

这样一来,使用 :normal 1,3 dd 命令并没有删除掉原始文件中的1、2、3行,而是删除了原始文件中的1、3、5行。

vim-normal命令

:normal 命令只有在与会更改文件行信息的普通模式命令结合才会出现上述的副作用,其他情况下的命令效果都是非常容易理解的。例如,命令 :normal 1,3 yy 会复制当前文件1~3行的内容到Vim无名寄存器。

上述 :normal 命令的执行机制与Vim的 :global 命令是完全不一样的。:[range]g[lobal]/{pattern}/[cmd] 命令是在 range 指定的范围内,对匹配 pattern 的行进行标记,只要文件行上的标记未被移除,就会在该行执行 cmd。

1. 奇偶行删除

本文继续介绍使用Vim的 :normal 命令来提取所有奇数行或所有偶数行文本,达到奇偶行删除/奇偶行分离的目的。

仍然假设有一段如下的《泰坦尼克号》电影对白,需要分别提取出ROSE和JACK的所有台词,并分别保存到两个文件中。

1
2
3
4
5
6
7
8
9
ROSE: I love you Jack.
JACK: No... don't say your good-byes, Rose. Don't you give up. Don't do it.
ROSE: I'm so cold.
JACK: You're going to get out of this... you're going to go on and you're going to make babies and watch them grow and you're going to die an old lady, warm in your bed. Not here. Not this night. Do you understand me?
ROSE: I can't feel my body.
JACK: You must do me this honor... promise me you will survive... that you will never give up... no matter what happens... no matter how hopeless... promise me now, and never let go of that promise.
ROSE: I promise.
JACK: Never let go.
ROSE: I promise. I will never let go, Jack. I'll never let go.

关于奇偶行删除和提取的具体需求分析,已经在上文”Vim处理文本之奇偶行删除”进行过介绍,本文因此不再赘述,不了解背景的同学建议先阅读Vim处理文本之奇偶行删除

2. :%normmal jdd 删除偶数行

根据前面对 normal 命令的介绍,:%normmal jdd 表示在当前文档的所有行( % ) 上执行 jdd 命令。jdd 是Vim普通模式下的一个命令组合,表示将光标从所在当前行下移一行 ( j ) 后执行删除行操作 ( dd )。

同样地,根据前面的分析,在第一行执行完 jdd 删除第行后,原始文件的第三行变成了第二行,在新的第二行继续执行 jdd 会删除原始文件的第行……直到原始文件的最后一行上执行 jdd 时,由于执行 j 命令出错 (无法再移动) 而终止 normal 命令的执行。从而完成了删除原始文件所有偶数行的操作。

vim奇偶行删除

3. :%normmal jkdd 删除奇数行

在分析 :%normmal jkdd 为什么能删除奇数行之前,需要再强调一下:normal 命令是纯粹根据行数来运行的,如果使用 % 来指定操作当前文档所有行,那么即便原始文件的最后一行已经执行过 normal 命令,只要不报错,normal 都会重复执行。

因此,出乎你的意料,在本文开头介绍的包含6行文本的 test.txt 文件中执行Vim命令 :%normal dd 时,确实会删除该文档的所有行,但是删除顺序却是: 第1行被删除、第3行被删除、第5行被删除、第6行被删除、第4行被删除、第2行被删除。

出现上述现象的原因是:normal 命令在执行到末行的时候并不会停止,而是会不断的重复执行。

那么有什么办法停止 normal 命令的执行呢?有!那就是出错。

normal 命令在执行到末行的时候,遇到出错就会自行停止。

jkdd 实际上就是我们人为加上的一个 错误,在除文件末行外的其他行执行 jkdd 命令时,会先将光标从当前行下移一行 ( j ),然后再将光标上移一行 ( k ),这么一下一上操作后,光标仍然处于原始行上,此时再执行 dd 则会删除当前行。因此,从文件第一行开始执行 jkdd 命令时,会逐步删除原始文件中的奇数行。

当删除到原始文件的末行时,此时因为已经位于末行而没法执行 j 操作而错误,所以 normal 命令就会停止,从而阻止了前面介绍的执行到末尾行后重复执行删除操作,最终导致整个文件都被删除的结果。

vim奇偶行删除

:%normal jkdd 会使得原始文件的末行无法被删除,因此,如果原始文件总行数是奇数,则最后一个奇数行无法被删除。

4. 小结

介绍了使用Vim :normal 命令来实现奇偶行删除或奇偶行分离,重点需要理解 normal 命令的执行原理和执行流程:normal 命令纯粹根据 range 指定的行数来逐行执行Vim普通模式下的命令,如果需要停止 normal 命令的执行,可以通过出错实现。

嗯,扫一扫就可以找到小女子我啦~