Saving when you forget to `sudo vim`

  ·   4 min read

Picture this—you are plugged in, furiously editing a config file in Vim over an SSH session to fix some issues in staging. Minutes pass by and you have keyed in some changes that might do the trick. You scroll through one last time before typing :w and Enter. Whoops! You annoyingly realize you don’t have the permissions to modify the file directly. You were supposed to run sudo vim to edit the goddamn thing!

Drat! You are panicking, trying to recall which lines had your changes so that you can stash them somewhere. You’ll then have to apply your changes in a new editor session but this time, remembering to run sudo vim /etc/precious.config.

Pain. You can probably relate to this. Especially if your work involves SSH-ing into a remote server where you have little control. Sure, if you had free reins to chown and chmod the file in question, you can dodge the troubles but life’s not always that easy.

Can we dig ourselves out of this pickle? Without the laborious copy-pasta? Well, I got annoyed enough to wonder and I googled it out.

All you have to do is type the following when in normal mode:

:w !sudo tee %

For subsequent edits, you don’t need to type it out again. Vim’s command-line history (like your shell) to the rescue—type :, hit Up arrow key and press Enter.

If you’re a Vim noob, here’s a quick refresher:

  • Normal mode: The mode you’re in when you open a file in Vim and move around.
  • Insert mode: When you press i to edit the file.
  • Command-line mode: When you are in normal mode and you try to quit Vim or save the file, you type :. E.g. :wq or :e file2.txt.

Try it out on a file where you’d normally use sudo (or make one, to be on the safer side). Make some changes and then enter the above command and press Enter. Enter your password if prompted. Then, Vim would throw a warning:

W12: Warning: File "precious.config" has changed and the buffer was changed in Vim as well
See ":help W12" for more info.
[O]K, (L)oad File, Load File (a)nd Options: 

Press L to load and press Enter. That’s it. You can validate by opening the same file elsewhere and see that the changes are present.

What black magic is this?

Let’s first try to understand the different pieces and then look at the puzzle.

You should be familiar with :w which we use to save our changes. Turns out, you can pass an argument to :w like so:

:w new-file-name.extension

And it would work like the Save As function in your GUI editors.

In Vim, you can execute shell commands in command-line mode by prefixing the command with a !. For example, you wanted to list the files in current directory without leaving Vim:

:!ls -la

Or you want to know your username:

:!whoami

Final piece of the puzzle is the tee command. The easy explanation is that you can use it to write something to stdout and a file at the same time1. Suppose you’re running a Python program which produces a lot of logs. You want to observe the logs in stdout but also save it to a file in case you want to debug later. Normally, you can only do one or the other. But, with tee, you can do both:

python app.py | tee app-logs.txt

This will print logs to your console, but at the same time, it also saves them to a file named app-logs.txt. Nifty, right?

A nice aspect of :w is that you don’t have to pass a filename. It can also act like a pipe which outputs contents of current buffer into something else, like our ! shell commands. Like Unix pipes.

A buffer in this context refers to the contents of the currently opened file.

Lastly, % in command-line mode refers to the current filename in this case.

Let’s go over the trick again:

:w !sudo tee %

…and try to read it. :w pipes the contents of the current buffer into the shell command sudo tee % where % will be substituted with the filename. That’s it.

Full credits go to @nathan-long on StackOverflow who wrote this excellent answer. If you’re still confused, give it a read. I wanted to write this down for my own reference and to drive the point home.

There is a better way

You can avoid getting into this jam by practising to type:

sudoedit filename

…instead of directly using Vim.

sudoedit is equivalent to running sudo $EDITOR, but easier to type out. Though, ensure you have the EDITOR environment variable set to your favorite editor, which would be vim in this case.

I’ve been getting the hang of using it for files I commonly edit like /etc/fstab but I must admit, I still forget to do it and that’s where the above trick comes in handy.

~

Hopefully, this trick will come in handy. Let me know if you think you got better ways to deal with the problem.