Generally you can select a visual block for the first n characters of a range of lines, and just press c, insert characters, and press Esc to have it apply to all.

However, this method is rarely possible for all the latter characters, because lines of code are likely to vary in length.

How can I easily change the last three characters of a range of lines?

4 Answers 4


It would not work all the time, but maybe you could temporarily right-align the right border of the code.

Suppose you have the following code containing 3 lines, each with the same level of indentation (8):

        here is foo
        here will be foo
        here was foo

And you want to change foo with bar. You could use the following commands:

  • vip = select the current paragraph
  • :right = right-align it
  • ff = move the cursor on the f character
  • C-v G = go into visual block mode and select the column of f characters
  • C = cut everything after the column
  • bar Esc = insert bar and escape to go back in normal mode
  • gv = visually select the last changed text (the bar column)
  • :left 8 = left-align the paragraph, giving it back its original level of indentation 8

It would look like this: enter image description here

If however, the lines in the block have different levels of indentation, then I don't think this solution can work, as you would probably have to remember the original level of each line, then fix it afterwards.

  • 1
    Now this is a creative solution. I actually would like to hotkey this, so the orientation is toggled on a key press.
    – Anon
    Jun 22, 2016 at 15:43
  • 2
    @Akiva To toggle the alignment, you would have to define a test to check whether the visually selected lines are left or right-aligned. Maybe you could compare the level of indentation of the first visually selected line with an arbitrary big number: xno <expr> ,x indent("'<") < 40 ? ':right<CR>' : ':left<CR>'. With this mapping, if you hit ,x on a visual selection while the first line has a level of indentation lower than 40, it should right-align it, otherwise left-align. But then your text will lose its original level of indentation. Jun 22, 2016 at 16:00
  • 1
    @Akiva For something a little more robust, you could try this: vpaste.net/0xAhN This other mapping should let you choose a level of indentation, when you realign your visual selection to the left. Jun 22, 2016 at 16:00
  • 2
    @Akiva I'm not sure this is what you want, but you could right-align the whole buffer by passing the range % to the :right command: :%ri. (:ri is the short form of :right). For the mapping, the previous one was still not very good because it shouldn't ask you what was the original level of indentation. It should be able to remember it itself. This one should do it: vpaste.net/AVw7b But it probably needs to be improved in other ways I didn't think of. Jun 22, 2016 at 16:15
  • 1
    @Akiva Ah yes I didn't think about that. But then we're not in visual mode anymore. But yes, I think it's a good idea. You would type: :%ri to right-align. Do your changes. Type: :%le to left-align. Then, gg=G or :norm! gg=G to auto-indent the whole buffer. For visual mode, you would have to adapt the range. In particular the last command would become: :norm! '<='> Jun 22, 2016 at 17:38

Building on @statox's answer,

  • \v very magic option, see :h \v for more info
  • .{3}$ last 3 characters of line
  • foo desired replacement string

EDIT Here is a better solution than the one I gave previously:

'<,'>g/.*/norm! $4hCfoo
  • '<,'> apply the command to the visual selection
  • g/.*/ apply the global command on all the lines (of the visual selection)
  • norm! interpret the following string as a normal command
  • $4hCfoo go 4 characters before the end of the line and replace them by foo

EDIT 2 And another one for the fun:

'<,'>g/.*/execute "s/\\%" . (len(getline('.'))-4) . "c.*$/toto"

I think it is overcomplicated but is interesting to understand the behavior of the global command:

  • '<,'> Apply on the visual selection
  • g/.*/ Apply the global command on all the lines of the visual selection
  • execute "..." Execute the following string as a command (We use it to insert a calculation)
  • s/ Aplly a substitution on the following pattern
  • \\%" Beginning of the pattern of the form \%Xc.*$ i.e. from the Xth column to the end of the line
  • . (len(getline('.'))-4) . Calculate the number of the column based on the size of the line minus the number of column to change
  • "c.*$ end of the pattern
  • "toto" The string to use as replacement

The interesting part is that to be able to use . (len(getline('.'))-4) . in the substitution you have to use the global command:

The global command will execute the command on each selected lines one after one, so getline('.') is updated on each line. If you only used

execute "'<,'>s/\\%" . (len(getline('.'))-4) . "c.*$/toto"

The getline('.') would only be the current line and thus you would get unexpected results.

There are probably some better solutions but you could do it using a macro. For example to replace the last 4 characters by foo:

  • qa record a macro
  • $4h go 4 character before the end of the line
  • C remove the end of the line and enter insert mode
  • foo the text to insert
  • <Esc> leave insert mode
  • j go to the next line
  • q stop recording the macro

You can then go to the first line and use 5@a to apply the macro on the 5 next lines.

  • 4
    wouldn't '<,'>s/\v.{4}$/foo/ be even simpler?
    – Sundeep
    Jun 22, 2016 at 11:45
  • 2
    @spasic Ahah I've never been good at vim golf. Indeed it is a way better solution, you should make it an answer
    – statox
    Jun 22, 2016 at 11:49

I would approach it with a macro + visual selection.

  1. start recording a macro
  2. do the changes
  3. stop the macro recording
  4. Visual select all lines that needs this change
  5. apply the macro

That would look the following:


record the macro (qq) with the change ($Cfoo) end the macro (q)

Visual select with V followed by the move keys to select the lines of interest. Then apply the macro for selection:

:normal @q

Note: caused by the visual selection, after pressing : you see the following :'<,'> there you just append normal @q. So that the whole command reads like:

:'<,'>normal @q

Now all lines have been changed. This pattern works for so manny repetitive tasks, not only EOL editing.


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.