How does the vim "write with sudo" trick work?
In :w !sudo tee %
...
%
means "the current file"
As eugene y pointed out, %
does indeed mean "the current file name", which is passed to tee
so that it knows which file to overwrite.
(In substitution commands, it's slightly different; as :help :%
shows, it's equal to 1,$ (the entire file)
(thanks to @Orafu for pointing out that this does not evaluate to the filename). For example, :%s/foo/bar
means "in the current file, replace occurrences of foo
with bar
." If you highlight some text before typing :s
, you'll see that the highlighted lines take the place of %
as your substitution range.)
:w
isn't updating your file
One confusing part of this trick is that you might think :w
is modifying your file, but it isn't. If you opened and modified file1.txt
, then ran :w file2.txt
, it would be a "save as"; file1.txt
wouldn't be modified, but the current buffer contents would be sent to file2.txt
.
Instead of file2.txt
, you can substitute a shell command to receive the buffer contents. For instance, :w !cat
will just display the contents.
If Vim wasn't run with sudo access, its :w
can't modify a protected file, but if it passes the buffer contents to the shell, a command in the shell can be run with sudo. In this case, we use tee
.
Understanding tee
As for tee
, picture the tee
command as a T-shaped pipe in a normal bash piping situation: it directs output to specified file(s) and also sends it to standard output, which can be captured by the next piped command.
For example, in ps -ax | tee processes.txt | grep 'foo'
, the list of processes will be written to a text file and passed along to grep
.
+-----------+ tee +------------+
| | -------- | |
| ps -ax | -------- | grep 'foo' |
| | || | |
+-----------+ || +------------+
||
+---------------+
| |
| processes.txt |
| |
+---------------+
(Diagram created with Asciiflow.)
See the tee
man page for more info.
Tee as a hack
In the situation your question describes, using tee
is a hack because we're ignoring half of what it does. sudo tee
writes to our file and also sends the buffer contents to standard output, but we ignore standard output. We don't need to pass anything to another piped command in this case; we're just using tee
as an alternate way of writing a file and so that we can call it with sudo
.
Making this trick easy
You can add this to your .vimrc
to make this trick easy-to-use: just type :w!!
.
" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %
The > /dev/null
part explicitly throws away the standard output, since, as I said, we don't need to pass anything to another piped command.
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 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 toNULL
to make a clean command -
<bar> edit!
: this trick is the cherry of the cake: it calls also theedit
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:
- SuperUser: force vim to write a file