99

I'd like to run a macro on every line in a selection, rather than totalling up the number of lines in my head. For instance, I might write a macro to transform:

Last, First

Into

First Last

and I'd like it to run on all these lines:

Stewart, John 
Pumpkin, Freddy
Mai, Stefan
...

Any ideas Vim gurus?

EDIT: This is just an example, obviously this is trivialy regexable, but there are other instances that come up that aren't quite so easy that I'd prefer to use macros.

4
  • 1
    To be honest, I would probably do this particular task with regex. But the question is still valid for more complex cases.
    – Jamie Wong
    Jul 26, 2010 at 18:50
  • Why vim? In my opinion sed is more appriopriate.
    – kokosing
    Jul 26, 2010 at 18:54
  • 9
    @kogut : Why exit your editor to edit your text with a one line regex?
    – Stephen
    Jul 26, 2010 at 18:56
  • 1
    Ok, You have got the point ;)
    – kokosing
    Jul 26, 2010 at 20:03

3 Answers 3

178

Suppose you had a macro q that ran (and remained) on a single line. Then you could run it on every line in your selection with:

:'<,'>normal @q

(if you already have a group of lines selected, hitting : produces :'<,'> on the command line)

For example, the following macro capitalizes every word but the first on a line:

:let @q="^dwgU$P"

So running it on the following (where the + lines are selected)

 0000: a long long time ago
 0001: in a galaxy far away
+0002: naboo was under an attack
+0003: and i thought me and qui-gon jinn
+0004: could talk the federation in
 0005: to maybe cutting them a little slack.

With the above normal @q command, produces:

 0000: a long long time ago
 0001: in a galaxy far away
 0002: naboo WAS UNDER AN ATTACK
 0003: and I THOUGHT ME AND QUI-GON JINN
 0004: could TALK THE FEDERATION IN
 0005: to maybe cutting them a little slack.
7
  • @Roger Pate: well, yes if you wanted to golf it :)
    – rampion
    Jul 27, 2010 at 13:15
  • 1
    @rampion: is there a typo in your command? for me it only works like this: :'<,'>normal! @q
    – oliver
    Jul 23, 2011 at 15:29
  • 3
    @oliver: check your mappings - you might have remapped @q. From :help normal - "If the [!] is given, mappings will not be used."
    – rampion
    Jul 23, 2011 at 16:11
  • 2
    @rampion: damn it ... you're right! I use yankring and apparently there is a problem with this: ... You cannot execute a macro with ":normal @a". It is still not possible, but you can execute it with ":normal! @a" (from the help docs of yankring)
    – oliver
    Jul 23, 2011 at 18:00
  • 1
    I personally like to map @ in visual mode like this: vnoremap @ :normal @. That way you can still do @a like you'd expect (albeit you have to hit enter afterwards :( )
    – LondonRob
    Nov 3, 2020 at 11:57
20

Select the lines then press : to enter command mode. Vim will automatically fill in '<,'>, which restricts the range to the selected lines. For your example you can use the :s command to do the swap:

:'<,'>s/\(\w\+\), \(\w\+\)/\2, \1/

This will swap two words separated by a comma on every line in the visual selection.

You can also use '< and '> like any other bookmark or line position, e.g. as part of a movement command, so in normal mode d'< will delete from the current cursor position to the start of the first line in the visual selection. The marks remain in effect even if the block is not longer visually highlighted.

If you want to replay a recorded macro on every line the you need to execute the macro with the :normal command. Unfortunately the :normal command does not operate on a range of lines, but you can fix that with the :global command. This runs an :ex command on every line that matches a regex, so you can do this:

:'<,'>g/^/ norm @a

Explanation:

:'<,'>       for every line in the visual block
g/^/         on every line that matches the regex /^/ - i.e. every line
norm         run in normal mode
@a           the macro recorded in a
2
  • This is better than the accepted answer because it supports macros that add new lines. Jun 30, 2017 at 9:12
  • @Dave, Kirby Maybe would be better to exchange ^ with . to make the macro run only on lines that has some text, wouldn't? Sep 23, 2017 at 18:02
1

You can add the function below to your ~/.vimrc or simply copy it on your browser and running :@+

fun! RunMacroOverSelection(macroname)
    execute "'<,'>normal @". a:macroname
endfun
com -nargs=1 Rover :call RunMacroOverSelection(<f-args>)
nnoremap <leader>r :Rover<space>

The advantage is that you can apply any macro on the visual selection. You need to give the macro letter as argument, like:

:Rover a

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.