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.
- 6,693
- 6
- 32
- 37
3 Answers
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.
- 3,491
- 18
- 28
- 42,369
- 5
- 94
- 123
-
And what is `tee`? – CodyBugstein Dec 05 '19 at 18:07
-
`tee` is a Linux command used to split streams, like a "T-splitter" in plumbing. More info at Wikipedia: https://en.wikipedia.org/wiki/Tee_(command) – jalanb May 27 '21 at 13:50
% 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
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.
- 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