35

If you open a file that you don't have permission to write to in vim, then decide you need to change it, you can write your changes without exiting vim by doing :w !sudo tee %
I don't understand how this can work. Can you please dissect this?
I understand the :w part, it writes the current buffer to disk, assuming there already is a file name associated with it, right?
I also understand the ! which executes the sudo tee command and % represents the current buffer content right?
But still don't understand how this works.

Ali
  • 6,693
  • 6
  • 32
  • 37

3 Answers3

43

The structure :w !cmd means "write the current buffer piped through command". So you can do, for example :w !cat and it will pipe the buffer through cat.

Now % is the filename associated with the buffer

So :w !sudo tee % will pipe the contents of the buffer through sudo tee FILENAME. This effectively writes the contents of the buffer out to the file.

Zanna
  • 3,491
  • 18
  • 28
Stephen Harris
  • 42,369
  • 5
  • 94
  • 123
11

% represents the current buffer's filename, not its contents.

so :w !sudo tee % means pipe the current buffer to sudo tee [currentfilename].

tee is being run as root, so has write access to save its stdin to the file.

See also https://stackoverflow.com/questions/2600783/how-does-the-vim-write-with-sudo-trick-work

cas
  • 1
  • 7
  • 119
  • 185
4

Not quite right!

!command runs command as a filter command, which get text from stdin, do something and output to stdout.

By using w, you pushed file content to stdin of sudo tee %. % is special register in vim, which holds the name of current file.

So you got sudo tee FILENAME, which will push tee stdin - file content - to current file.

cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • 2
    `:!command` is a filter (cf. `:h !`), while `:w !command` is not, it just executes `command` with the current file as `stdin` (cf. `:h :w_c`). To wit: `:w !sed /./d` doesn't change the content of the current buffer. But the recipe is indeed not quite right for another reason, `%` needs to be escaped: `:exec 'w !sudo tee ' . shellescape(expand('%', 1))`. The original command doesn't work with, say, filenames with spaces. – Satō Katsura Aug 04 '16 at 12:11