g如何在Vim ex命令脚本中循环

How g makes loop in Vim ex command script

考虑以下 Vim ex 命令,

:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1

它用选定行中的有序数字替换标题破折号。

我不明白为什么这个命令从所选行的第一行到最后一行循环工作。也就是说,g怎么会一遍又一遍地重复let i+=1

这是 :global 命令的一般模式:

:g/foo/command

因为第二个分隔符之后的所有内容都被视为一个命令,每次执行命令时计数器都会递增:每个匹配行一次。

全局命令的模式是:

:range g[lobal][!]/pattern/cmd

全局命令的工作方式是首先扫描行的 [range] 并标记出现匹配的每一行。在第二次扫描中,[cmd] 为每个标记的行执行,并在前面加上行号。如果一行被更改或删除,它的标记就会消失。 [range] 的默认值是整个文件。 (有关详细信息,请参阅 http://vimregex.com/#global

现在我们来分析一下

:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1

循序渐进。

  1. let i=1 是执行的单个命令,用于设置循环的基本编号。我们可以在一开始就单独执行它。然后 '<,'>g/^/ s/^\ *-/\=i/ | let i+=1 看起来更像一个全局命令。
  2. '<,'>g 定义范围。 '<代表选中区域的第一行,'>代表选中区域的最后一行。 (:help '< 了解更多详情)
  3. ^ 当然匹配范围内的每一行。
  4. s/^\ *-/\=i/ | let i+=1是[cmd],它执行的次数等于选中区域的行数,这是造成循环的最重要原因。
  5. |之前的部分是一个典型的替代命令:range s[ubstitute]/pattern/string/(详情见http://vimregex.com/#substitute
  6. ^\ *- 匹配 0 个或多个空格,后跟行首的破折号。我们用 \=i 代替这个模式。 (:help :s\= 了解更多详情)
  7. s/^\ *-/\=i/后执行let i+=1。然后下一行,...,直到选中区域的最后一行。
  8. 为了更好的理解s/^\ *-/\=i/ | let i+=1是一个整体的[cmd],我们可以改变两个[sub-cmd]的顺序,得到let i+=1 | s/^\ *-/\=i/。但是同样的效果,最开始的let i=0是必不可少的。