[vim] How does the vim "write with sudo" trick work?

Many of you have probably seen the command that allows you to write on a file that needs root permission, even when you forgot to open vim with sudo:

:w !sudo tee %

The thing is that I don't get what is exactly happening here.

I have already figured this: w is for this

                                                        *:w_c* *:write_c*
:[range]w[rite] [++opt] !{cmd}
                        Execute {cmd} with [range] lines as standard input
                        (note the space in front of the '!').  {cmd} is
                        executed like with ":!{cmd}", any '!' is replaced with
                        the previous command |:!|.

so it passes all the lines as standard input.

The !sudo tee part calls tee with administrator privileges.

For all to make sense, the % should output the filename (as a parameter for tee), but I can't find references on the help for this behavior.

tl;dr Could someone help me dissect this command?

This question is related to vim sudo

The answer is


:w - Write a file.

!sudo - Call shell sudo command.

tee - The output of write (vim :w) command redirected using tee. The % is nothing but current file name i.e. /etc/apache2/conf.d/mediawiki.conf. In other words tee command is run as root and it takes standard input and write it to a file represented by %. However, this will prompt to reload file again (hit L to load changes in vim itself):

tutorial link


I'd like to suggest another approach to the "Oups I forgot to write sudo while opening my file" issue:

Instead of receiving a permission denied, and having to type :w!!, I find it more elegant to have a conditional vim command that does sudo vim if file owner is root.

This is as easy to implement (there might even be more elegant implementations, I'm clearly not a bash-guru):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

And it works really well.

This is a more bash-centered approach than a vim-one so not everybody might like it.

Of course:

  • there are use cases where it will fail (when file owner is not root but requires sudo, but the function can be edited anyway)
  • it doesn't make sense when using vim for reading-only a file (as far as I'm concerned, I use tail or cat for small files)

But I find this brings a much better dev user experience, which is something that IMHO tends to be forgotten when using bash. :-)


A summary (and very minor improvement) on the most common answers that I found for this as at 2020.

tl;dr

Call with :w!! or :W!!. After it expands, press enter.

  • If you are too slow in typing the !! after the w/W, it will not expand and might report: E492: Not an editor command: W!!

NOTE Use which tee output to replace /usr/bin/tee if it differs in your case.

Put these in your ~/.vimrc file:

    " Silent version of the super user edit, sudo tee trick.
    cnoremap W!! execute 'silent! write !sudo /usr/bin/tee "%" >/dev/null' <bar> edit!
    " Talkative version of the super user edit, sudo tee trick.
    cmap w!! w !sudo /usr/bin/tee >/dev/null "%"

More Info:

First, the linked answer below was about the only other that seemed to mitigate most known problems and differ in any significant way from the others. Worth reading: https://stackoverflow.com/a/12870763/2927555

My answer above was pulled together from multiple suggestions on the conventional sudo tee theme and thus very slightly improves on the most common answers I found. My version above:

  • Works with whitespace in file names

  • Mitigates path modification attacks by specifying the full path to tee.

  • Gives you two mappings, W!! for silent execution, and w!! for not silent, i.e Talkative :-)

  • The difference in using the non-silent version is that you get to choose between [O]k and [L]oad. If you don't care, use the silent version.

    • [O]k - Preserves your undo history, but will cause you to get warned when you try to quit. You have to use :q! to quit.
    • [L]oad - Erases your undo history and resets the "modified flag" allowing you to exit without being warned to save changes.

Information for the above was drawn from a bunch of other answers and comments on this, but notably:

Dr Beco's answer: https://stackoverflow.com/a/48237738/2927555

idbrii's comment to this: https://stackoverflow.com/a/25010815/2927555

Han Seoul-Oh's comment to this: How does the vim "write with sudo" trick work?

Bruno Bronosky comment to this: https://serverfault.com/a/22576/195239

This answer also explains why the apparently most simple approach is not such a good idea: https://serverfault.com/a/26334/195239


This also works well:

:w !sudo sh -c "cat > %"

This is inspired by the comment of @Nathan Long.

NOTICE:

" must be used instead of ' because we want % to be expanded before passing to shell.


The accepted answer covers it all, so I'll just give another example of a shortcut that I use, for the record.

Add it to your etc/vim/vimrc (or ~/.vimrc):

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

Where:

  • cnoremap: tells vim that the following shortcut is to be associated in the command line.
  • w!!: the shortcut itself.
  • execute '...': a command that execute the following string.
  • silent!: run it silently
  • write !sudo tee % >/dev/null: the OP question, added a redirection of messages to NULL to make a clean command
  • <bar> edit!: this trick is the cherry of the cake: it calls also the edit command to reload the buffer and then avoid messages such as the buffer has changed. <bar> is how to write the pipe symbol to separate two commands here.

Hope it helps. See also for other problems:


FOR NEOVIM

Due to problems with interactive calls (https://github.com/neovim/neovim/issues/1716), I am using this for neovim, based on Dr Beco's answer:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

This will open a dialog using ssh-askpass asking for the sudo password.


In the executed command line, % stands for the current file name. This is documented in :help cmdline-special:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

As you've already found out, :w !cmd pipes the contents of the current buffer to another command. What tee does is copy standard input to one or more files, and also to standard output. Therefore, :w !sudo tee % > /dev/null effectively writes the contents of the current buffer to the current file while being root. Another command that can be used for this is dd:

:w !sudo dd of=% > /dev/null

As a shortcut, you can add this mapping to your .vimrc:

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

With the above you can type :w!!<Enter> to save the file as root.


The only problem with cnoremap w!! is that it replaces w with ! (and hangs until you type the next char) whenever you type w! at the : command prompt. Like when you want to actually force-save with w!. Also, even if it's not the first thing after :.

Therefore I would suggest mapping it to something like <Fn>w. I personally have mapleader = F1, so I'm using <Leader>w.