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?
: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):
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:
root
but requires sudo
, but the function can be edited anyway) 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.
Call with :w!!
or :W!!
.
After it expands, press enter
.
!!
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 "%"
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.
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
.
Source: Stackoverflow.com