It's pretty common when programming or opening text files to encounter files with trailing whitespace at the end of a line. vim has a way to show this by setting the trail option in the listchars option and then turning list on.

However, what's the easiest way to eliminate that trailing whitespace globally across the whole of a file (ideally without a plugin)?

  • The corresponding Stack Overflow question is How to remove trailing white spaces using a regular expression without removing empty lines (code cleaning). Commented Jul 5, 2017 at 20:57
  • Here is a doc entry on topic.
    – user15181
    Commented Aug 6, 2018 at 8:40
  • 1
    If you have installed vim-faq, you can get an offline answer there: :h vim-faq and search /trailing. The hard to memorize tag is :h faq-12.1.
    – Hotschke
    Commented Jul 23, 2019 at 7:39
  • A lot of complicated answers below, for me :g/ $/norm $diw seems to do the job. This moves to the end of lines that end with a whitespace and "deletes inside word". Tabs and spaces appear to be considered as a single word, so as long as there is at least one trailing space this does the trick. I may be missing some cases though?
    – Simon
    Commented Apr 6, 2022 at 4:09
  • 1
    :g/\s$/norm $diw appears to deal with other whitespace characters as well.
    – Simon
    Commented Apr 6, 2022 at 4:10

6 Answers 6


Use a keybinding to strip all trailing whitespace

Since some pages that I edit actually need trailing whitespaces (e.g. markdown) and others don't, I set up a keybinding to F5 so that it's trivial to do without being automatic. To do so, add the code below (from vim.wikia) or some variation of it to your .vimrc:

"Remove all trailing whitespace by pressing F5
nnoremap <F5> :let _s=@/<Bar>:%s/\s\+$//e<Bar>:let @/=_s<Bar><CR>
  • nnoremap <F5> does a nonrecursive mapping to the key F5 in normal mode
  • :let _s=@/ stores the last search term (from the macro @/) in the variable _s
  • <Bar> Functions as a pipe symbol | to separate commands, however | would end a command in this context, so <Bar> must be used instead.
  • :%s/\s\+$//e searches for trailing whitespace and deletes it everywhere in the buffer (see CarpetSmoker's answer for a detailed breakdown of this expression)
  • let @/=_s restores your last search term to the macro @/, so that it will be available the next time you hit n.
  • <CR> ends the mapping

... or be more selective

If you have cases in which you don't want to strip all of the trailing whitespace, you can use a pattern to be more selective. For example, the following code shows how I strip trailing whitespace only if it comes after a semicolon (here it's tied to F8).

nnoremap <F8> :let _s=@/<Bar>:%s/;\s\+$/;/e<Bar>:let @/=_s<Bar><CR>

This is useful if, like me, you have some files with markdown-like heredocs interspersed among semicolon-terminated programming statements.

  • 7
    Try :keeppatterns to prevent overriding @/. And also take a look at :keepjumps.
    – Bohr
    Commented Mar 7, 2015 at 6:14
  • @Bohr What version of vim are you using? I tried :help keeppattern and didn't get anything. Commented Mar 9, 2015 at 22:01
  • @ChristopherBottoms At least version 7.4.155.
    – Bohr
    Commented Mar 10, 2015 at 4:51
  • @Bohr. Thanks! Come to find out I was still using 7.4.0. I installed the latest version and it's available. Commented Mar 10, 2015 at 14:08
  • 2
    You can get the same effect by wrapping this command in a function, since then the last search term is automatically restored :-) This way you don't have to muck about with :nohl either, if if you were highlighting something, it will keep highlighting it (see my updated answer). Commented Jun 25, 2015 at 16:39

The "simplest" way is to just use :substitute:

  • :%s to run :substitute over the range %, which is the entire buffer.
  • \s to match all whitespace characters.
  • \+ to match 1 or more of them.
  • $ to anchor at the end of the line.
  • The e flag to not give an error if there is no match (i.e. the file is already without trailing whitespace).

However, this is probably not the "best" way as it causes two side-effects:

  1. it moves the cursor to the last match;
  2. it adds the command to the history and search history;
  3. it resets the last search term.

You can fix this with a small function:

fun! TrimWhitespace()
    let l:save = winsaveview()
    keeppatterns %s/\s\+$//e
    call winrestview(l:save)

And then use it like:

:call TrimWhitespace()
  1. The winsaveview() will save the current "view", which includes the cursor position, folds, jumps, etc. The winrestview() at the end will restore this from the saved variable.
  2. The :keeppatterns prevents the \s\+$ pattern from being added to the search history.
  3. The last-used search term is automatically restored after leaving a function, so we don't have to do anything else for this.

Since this is somewhat annoying to type :call all the time, you can define a command:

command! TrimWhitespace call TrimWhitespace()

Which can be be used without the :call:


And you can of course bind it to a key:

:noremap <Leader>w :call TrimWhitespace()<CR>

Some people like to automatically do this before they write a file to disk, like so:

autocmd BufWritePre * call TrimWhitespace()

I don't like it, as some formats require trailing whitespace (such as Markdown), and on some other occasions you even want trailing whitespace in your code (such as formatting an email, and using the --<Space> marker to indicate the start of a signature).

You can skip binary files with:

autocmd BufWritePre * if !&binary | call TrimWhitespace() | endif

Or other filestypes:

autocmd BufWritePre * if !&binary && &ft !=# 'mail' 
                   \|   call TrimWhitespace()
                   \| endif

Shameless plug mode: a while ago I wrote a little Python script to clean up whitespace for an entire project at once.

  • 2
    If you don't want to create a function to move to the previous position you could just press ​`​ twice after the replace is finished. This opens the possibility to create a oneliner like this: %s/\s\+$//e | exe "normal ``" Commented Mar 8, 2016 at 10:00
  • 1
    @NeaţuOvidiuGabriel, of course then the double backtick will not work the same after the oneliner is executed. ;)
    – Wildcard
    Commented Jan 10, 2017 at 2:04
  • 1
    Similar: stackoverflow.com/a/1618401. But I like Martin's code more.
    – john c. j.
    Commented Jan 31, 2019 at 11:25
  • I do it the same way: :%s/\s\+$//e and before I convert Tabs to spaces: :%retab
    – WalterH
    Commented Apr 30, 2022 at 14:21
  • FWIW matching zero or more * is the same as \+ like so :s/\s*$//e
    – CervEd
    Commented Jan 27 at 11:29

To delete all trailing whitespace (at the end of each line), you can use the command:

:%s/ \+$//

To include tabs, use \s instead of space.

From the command-line:

$ ex +'%s/\s\+$//e' -cwq file.c

All the files in the current directory (recursively use **/*.*):

$ ex +'bufdo!%s/\s\+$//e' -cxa *.*

Python way:

:py import vim
:pydo vim.current.buffer[linenr - 1] = vim.current.buffer[linenr - 1].strip()


:py import vim
:py for i, l in enumerate(vim.current.buffer): vim.current.buffer[i] = l.rstrip()

Use lstrip() for left strip (trailing), rstrip() for right strip (leading) or strip() to remove from both ends.

Here is useful function which removes superfluous white space from the end of a line which you can add to your .vimrc:

" Removes superfluous white space from the end of a line
function! RemoveWhiteSpace()

There is also DeleteTrailingWhitespace plugin for that.

Highlighting white spaces

To double-check if all trailing spaces are gone, use:

  1. Type / $ to find them. If there are some, vim would highlight them for you.

  2. Use colours to highlight them:

    :highlight ws ctermbg=red guibg=red
    :match ws /\s\+$/
  3. Use visible characters (source):

    :set encoding=utf-8
    :set listchars=trail:·
    :set list

See also: Highlight unwanted spaces

To highlight trailing whitespace by default, you may configure your .vimrc as follow:

highlight ws ctermbg=red guibg=red
match ws /\s\+$/
autocmd BufWinEnter * match ws /\s\+$/

Removing white spaces by default

If you would like to make sure that all trailing whitespace in a file are removed automatically on save, you may add the following command into your .vimrc:

autocmd BufWritePre *.c,*.php :%s/\s\+$//ge

which may not be recommended, as it'll unconditionally strip trailing whitespace from every file of that type saved by an user saves and there's not a good way to prevent that where trailing whitespace might be desired (which should be rare, but still.)

See also:


Exapnding on Christopher Bottoms's answer a bit: Jonathan Palardy wrote a good article on this. In it, he writes a functions, Preserve(command), that preserves the state of the editor (mainly cursor position and last search pattern) while running an arbitrary command:

function! Preserve(command)
  " Preparation: save window state
  let l:saved_winview = winsaveview()
  " Run the command:
  execute a:command
  " Clean up: restore previous window position
  call winrestview(l:saved_winview)

This has the advantage being multipurpose, for example you can use it to replace all trailing whitespace (as Jonathan does) by mapping this to :

nnoremap <F5> :call Preserve("%s/\\s\\+$//e")<CR>

You can also use it for a visual mode mapping to just remove the trailing spaces on visually selected lines:

xnoremap <F5> :call Preserve("'<,'>s/\\s\\+$//e")<CR>

And you can use it for other calls, like formatting the whole document using = while retaining your place (well use a different key this time so as not to conflict):

nnoremap <F6> :call Preserve("normal gg=G")<CR>

All and all, I've found the Preserve(command) function to be a nice tool to have.

  • 2
    The last used search term should be automatically preserved when you leave a function; so mucking about with @/ should not be required (in this case, anyway). Commented Jun 25, 2015 at 16:43
  • 3
    winsaveview() and winrestview() are far superior. Commented Mar 5, 2016 at 1:01
  • Quite right! Updated based on your feedback.
    – Alex
    Commented Dec 14, 2017 at 22:13

Another version of the StripTrailingSpaces function:

if !exists('*StripTrailingWhitespace')
    function! StripTrailingWhitespace() range
        if !&binary && &filetype != 'diff'
            call Preserve(":" . a:firstline . "," . a:lastline . "s/\\s\\+$//e")

" http://technotales.wordpress.com/2010/03/31/preserve-a-vim-function-that-keeps-your-state/
if !exists('*Preserve')
    function! Preserve(command)
            let l:win_view = winsaveview()
             "silent! keepjumps keeppatterns execute a:command
            silent! execute 'keeppatterns keepjumps ' . a:command
            call winrestview(l:win_view)

Actually there is a bug in this function (this one):

The position is not being kept because of the "range" option. It it is removed, and it works very well. However, I am sharing the code to receive some help.

As you can see, it also uses the Preserve function seen above, but in a slightly different fashion.

The difference here is that I can select a range of lines or a paragraph with vip and then the range :'<,'> will automatically appear at the command prompt.

The idea came from a Bez Hermoso post.

  • Re "I am sharing the code to receive some help": Are you asking a question? Commented Nov 5, 2019 at 20:14
  • Sorry for taking so long giving you feedback. The needed "Preserve" function was finally added. Now my answer makes some sense. It is an attempt to make getting rid of trailing spaces easier. Commented Nov 10, 2019 at 11:11

Kudos to all the previous answers. I really like the use of keeppatterns. I just want to add one more thing that no one else mentioned - the substitute command's confirmation flag. This could effectively handle the case where certain filetypes, or even specific lines in a file, shouldn't have trailing spaces removed. Here it is as an autocommand...

autocmd BufWritePre * let [v,c,l]=[winsaveview(),&cuc,&cul]
                  \ | set cuc cul
                  \ | keeppatterns %s/\s\+$//ec
                  \ | let [&cuc,&cul]=[c,l]
                  \ | call winrestview(v)
                  \ | unlet v l c


  1. Save the current window/cursor state, and the values of the cursorcolumn and cursorline settings.
  2. Turn on cursorcolumn and cursorline to make the next step easier to follow.
  3. Remove all trailing whitespace. The c flag at the end moves the cursor to each occurrence and asks for confirmation: replace with (y/n/a/q/l/^E/^Y)?
  4. Restore the cursorcolumn and cursorline settings to what they were beforehand.
  5. Restore the window/cursor state.
  6. Dump the variables we don't need anymore.
  • If you put the code in a function, it's easier to format and you can use l: variables so that you don't have to :unlet them
    – D. Ben Knoble
    Commented Jul 13, 2022 at 13:55

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