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

6
考虑以下Vim ex命令,
:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1

它可以在选定的行中将标题破折号替换为有序编号。

我不理解为什么这个命令会从选定行的第一行循环到最后一行。也就是说,如何让g一遍又一遍地重复执行let i+=1

2个回答

10
全局命令的模式如下:
:range g[lobal][!]/pattern/cmd

全局命令的工作方式是先扫描行的[范围]并标记每个匹配的行。在第二次扫描中,为每个已标记的行添加其行号并执行[cmd]。如果更改或删除了一行,则其标记消失。[范围]的默认值为整个文件。(有关更多详细信息,请参见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. ^\ *-匹配以零个或多个空格开头的连字符。我们用\=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=1是必要的。

有没有办法在\=i中加入其他字符?对我来说似乎不起作用。例如,:let i=1 | '<,'>g/^/ s/^\ *-/[\=i]/ | let i+=1 - Jake0x32
我建议查看:help sub-replace-expression以获取关于\=i的信息,因为:help :s\=目前会将您引导到那里。 - tmoore82
3
使用命令 :let i=1 | '<,'>g/^/ s/^\ *-/\="[" . i . "]"/ | let i+=1 可以实现该功能。 - toolchainX
非常感谢您!您详细的解释帮助我构想出替换增加数字的方法,而不需要使用可视'<,'>选择(即:let i=10800 | 1,$g/$/ s/$/\=i/ | let i+=10)。唯一无法实现的是在数字前面添加逗号... s/$/,\=i/没有起作用... 所以我只能分两步完成。 - Stephen P

1

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

:g/foo/command

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


第二个分隔符后的所有内容都被视为一个命令。这很有趣!感谢您回答,@romainl :D - George

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接