Consider the following Vim ex command,
:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1
It replaces the heading dash with ordered number in selected lines.
I don't understand why this command works as a loop from the first line to the last line of the selected lines. That is, how g
can repeat let i+=1
over and over again.
The g command first scans all lines in range and marks those that match pattern . It then iterates over the marked lines and executes cmd . (See multi-repeat in vim's documentation). If cmd happens to delete a marked line, the mark disappears.
An exclamation mark ( ! ) tells ex to create a shell and to regard what follows as a Unix command: :! command. So if you are editing and you want to check the time or date without exiting vi, you can enter: :!date.
The pattern of a global command is:
:range g[lobal][!]/pattern/cmd
The global commands work by first scanning through the [range] of of the lines and marking each line where a match occurs. In a second scan the [cmd] is executed for each marked line with its line number prepended. If a line is changed or deleted its mark disappears. The default for the [range] is the whole file. (see http://vimregex.com/#global for more details)
Now let's analyse
:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1
step by step.
let i=1
is a single command executed setting the basic number for the loop. We can just execute it alone at the very beginning. Then '<,'>g/^/ s/^\ *-/\=i/ | let i+=1
looks a little more like a global command.'<,'>g
defines the range. '<
represents the first line and '>
represents the last line of the selected area. (:help '<
for more details)^
of course matches every line in range.s/^\ *-/\=i/ | let i+=1
is the [cmd], the number of times it will be executed equals to the number of lines in the selected area, and this is the most important reason why the loop took place.|
is a typical substitute command :range s[ubstitute]/pattern/string/
(see http://vimregex.com/#substitute for more details)^\ *-
matches 0 or more whitespace followed by a dash at the beginning of a line. We substitute \=i
for this pattern. (:help :s\=
for more details)s/^\ *-/\=i/
, let i+=1
is executed. Then the next line, ... , till the last line of selected area.s/^\ *-/\=i/ | let i+=1
is a [cmd] as a whole, we can change the order of the two [sub-cmd], obtaining let i+=1 | s/^\ *-/\=i/
. But for the same effect, let i=0
at the very beginning is essential.This is the general pattern of a :global
command:
:g/foo/command
Because everything after the second separator is considered as one command, the counter is incremented each time the command is executed: one time for each matching line.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With