3 minutes
Macros with regular expressions in VIM
Say we want to replace some values in a file; however, rather than performing a direct replacement, we are interested in replacing values based on their relative positionto their containing objects, or within certain patterns accompanying each of them.
For example, the following file contains some information in a typical fortran
structure:
!- An example with objects and variables
Object A, !- Some comment or additional label for objects
Name A1, !- Name of object
10, !- First variable in object
9, !- Second variable in object
8, !- Third variable in object
7; !- Fourth variable in object
Object A, !- Some comment or additional label for objects
Name A2, !- Name of object
20, !- First variable in object
19, !- Second variable in object
18, !- Third variable in object
17; !- Fourth variable in object
Object A, !- Some comment or additional label for objects
Name A3, !- Name of object
10, !- First variable in object
19, !- Second variable in object
8, !- Third variable in object
17; !- Fourth variable in object
Object B, !- Some comment or additional label for objects
Name B1, !- Name of object
20, !- First variable in object
9, !- Second variable in object
8, !- Third variable in object
7; !- Fourth variable in object
Object C, !- Some comment or additional label for objects
Name C1, !- Name of object
10, !- First variable in object
9, !- Second variable in object
18, !- 3rd variable in object
7; !- Fourth variable in object
Object C, !- Some comment or additional label for objects
Name C2, !- Name of object
30, !- First variable in object
9, !- Second variable in object
8, !- Third variable in object
7; !- Fourth variable in object
And we want to replace the first variable in objects A and B, with 55.
To this end, we need to:
1. Define the logic
2. Translate the logic to a regex
command
3. Manage the repetition of this logic.
1. Define the logic
One way of doing this would be to search fo the common pattern: “First variable in object”, and then check if it is contained in either “Object A” or “Object B”.
Or the other way round, to search for “Object A” or “Object B” and delimit the pattern to the presence of “First variable in object”.
Let’s use the second alternative. First we need to find “Object A” or “Object B”, followed by any text. Second we need to stop the search until the pattern “First variable in object” appears. Because the later pattern appears multiple times, we delimit the second search to a range of lines so it doesn’t match a pattern in the following object.
2. Translate the logic to regex
command
A line with “Object A” or “Object B”, followed by any text:
/Object [BC].*\n
[BC] : B or C character
\n : a newline character (line ending)
Stop until the pattern “First variable” appears, in a range of 6 lines.
.*\(\_s.*\)\{0,6\}\_sFirst variable
.* : any character except new line, multiple times
\_s : a whitespace (space or tab) or newline character
\{0,6\} : a range of 6 lines
The full regular expression:
/Object [BC].*\n .*\(\_s.*\)\{0,6\}\_sFirst variable
3. Manage the repetition of this logic.
We record a macro (in t) to make the substitution.
qt
/Object [BC].*\n .*\(\_s.*\)\{0,6\}\_sFirst variable
2jwcw55<esc>j
q
2j : move two lines down
w : move one word
cw : change word
55 : new value
<esc> : return to command mode
j : move one line down
And recursively run the macro in the file
:%normal @t
Let’s now replace the third variable in all objects with 99.
qt
/Object.*\n .*\(\_s.*\)\{0,3\}\_svariable
2jwcw55<esc>j
q
:%normal @t
I hope this has been useful.