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?
Best Answer
In
:w !sudo tee %
...%
means "the current file"As eugene y pointed out,
%
does indeed mean "the current file name", which is passed totee
so that it knows which file to overwrite.(In substitution commands, it's slightly different; as
:help :%
shows, it'sequal 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 offoo
withbar
." 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 fileOne confusing part of this trick is that you might think
:w
is modifying your file, but it isn't. If you opened and modifiedfile1.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 tofile2.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 usetee
.Understanding tee
As for
tee
, picture thetee
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 togrep
.(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 usingtee
as an alternate way of writing a file and so that we can call it withsudo
.Making this trick easy
You can add this to your
.vimrc
to make this trick easy-to-use: just type:w!!
.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.