Blog Posts

rm -rf overdose

When a tiny error makes you lose time and money

The rm command on Unix and the -rf flags are like bread and butter. At least for me, it's hard to imagine running the rm command without those flags. As a matter of fact, it became like an instinct and almost as if the command was always rm -rf.

If you know what you are doing, you can be safe running the full command like that all the time, but I should know myself better and recognize that it is going to be way better for me to think twice before hitting the Enter key. Let me tell you about what happened to me a few weeks ago when I lost a whole day of progress on a project running on a Google Compute Cloud instance for which I was paying 1.5 USD an hour. Essentially, running an rm -rf command without giving it second thoughts made me lose around 15 USD. Which is not a lot of money compared to what happened to Pixar for running this exact same command a few years ago.

An introduction to rm

The way I usually used rm -rf is extremely simple:

rm -rf directory_to_delete/

I just specified the path to the directory I wanted to remove recursively and I'm done! Great đŸ’¯đŸ’¯đŸ’¯. To gain more context let's break the command step by step:

The rm utility attempts to remove the non-directory type files specified on the command line. If the permissions of the file do not permit writing, and the standard input device is a terminal, the user is prompted (on the standard error output) for confirmation.

So there you go, the basic command to remove files in Unix.

Don't try this at /home

Let's set up a test environment to show you the silly mistake I made the other day. I will spin up an Ubuntu container using Docker, but if you like you could use any Virtual Machine, Cloud Instance, a disk partition of your old laptop with a Linux distro you use for experiments, anything you like. Just avoid playing with rm on your main machine, especially near your important files, please 🙏.

I'll start by running an interactive Ubuntu session with docker and creating a basic file structure:

$ docker run -it --rm ubuntu
root@ubuntu:/# cd home/
root@ubuntu:/home# mkdir my-files
root@ubuntu:/home# cd my-files
root@ubuntu:/home/my-files# mkdir pictures
root@ubuntu:/home/my-files# mkdir music
root@ubuntu:/home/my-files# mkdir code
root@ubuntu:/home/my-files# mkdir docs
root@ubuntu:/home/my-files# cd docs
root@ubuntu:/home/my-files/docs# mkdir pdfs
root@ubuntu:/home/my-files/docs# mkdir business
root@ubuntu:/home/my-files/docs# mkdir tmp

OK, so far we are just creating a bunch of directories. The structure is as follows:

|- my-files
|   |- pictures
|   |- music
|   |- code
|   |- docs
|   |   |- pdfs
|   |   |- business
|   |   |- tmp

Now imagine that we would like to get rid of the tmp directory under the /docs directory (funnily enough, this was actually what I was trying to do!), we can achieve this by doing:

rm -rf /home/my-files/docs/tmp

We could also do it using relative paths, so for instance, if our current working directory is /home/my-files/docs we could do instead rm -rf tmp/.

Now, here comes what I did wrong the time I was trying to delete my tmp directory:

rm -rf /home/my-files/docs/tmp\

Can you spot the difference with the original command? It's just a character! Right before I pressed the Enter key I accidentally pressed the key right above it in my Macbook keyboard, the backslash character \. What's so special about the backslash character on a bash command? It escapes the next character to avoid it to be interpreted by the shell. So in my case when I pressed the Enter key my rm command didn't run yet.

Hmm. That could be potentially good, right? Actually, yes! That little mistake can give you one more chance to review that the line you've written is correct and if you spot any typo, disregard it before doing any damage 🙂. The problem is that, if you happen to not be paying enough attention (which was definitely my case), you could end up not realizing that your rm command didn't run yet and move to the next command as if everything went smoothly. And what happens if the next command you run is a cd (change directory)? 🤔 let's go ahead and try it:

root@ubuntu:/# rm -rf /home/my-files/docs/tmp\
> cd /home/my-files

Run it and... all of our folders are gone 😱

Wh...what happened?

Remember how we said that the \ made the shell ignore the next character? Well... That means that our previous command is equivalent to this:

rm -rf /home/my-files/docs/tmpcd /home/my-files

I didn't know that rm could be used to remove multiple directories at the same time, but it turns out it is possible. So here we are trying to delete two directories: /home/my-files/docs/tmpcd (which doesn't exist since the cd suffix shouldn't be there) and... /home/my-files 😱😱😱😱. So, even if I originally was extremely confident about running rm -rf /home/my-files/docs/tmp since of course I wanted to remove that directory and thus the -f flag wouldn't be a problem, I wasn't aware that the shell didn't run the command yet, so the cd /home/my-files of the next line wasn't interpreted as a change directory command.

What can I do to prevent this?

First of all, have frequent backups and test recover from them from time to time. If possible, make sure to have more than one physical copy of your files and also store what you can in a secure cloud. Mistakes, accidents, and disk issues happen and you never know when.

If your platform is running the GNU version of rm you can use the -I (uppercase i) flag instead of -f and you will receive a prompt before the actual execution of the destructive action (unless you are trying to remove less than 4 files and not recursively). It's similar to -i (lowercase i) but far less annoying and is perfect for the situation described in this post.

Let's see it in action:

root@ubuntu:/# rm -rI /home/my-files/docs/tmp\
> cd /home/my-files
rm: remove 2 arguments recursively?

If we were using the -I flag, we would be surprised to see the rm confirmation prompt after what we thought was a cd command. It gives us much higher chances of catching this kind of errors and in general, it gives us one more chance of thinking about what we are about to do.

BUT... it's not available by default on macOS ☚ī¸. The thing is that the rm implementation found on macOS is the BSD one, that doesn't support the -I flag. If you use homebrew you can install rm (and many other GNU coreutils) with brew install coreutils and the GNU rm version will be available as grm. You can then alias it to rm if you'd like.

If you would rather keep the native implementation of rm I would suggest using the Z shell as a shell with the oh-my-zsh framework installed. There, you can find many amazing themes to customize your shell with different backgrounds, icons and what's important for avoiding the mistake that concerns us the most in this post: the prompt.

I personally use the agnoster theme, and it gives you a really distinctive prompt that makes it far easier to me to identify when I'm continuing the same command of the previous line or if I'm actually in a new line.

Screenshot of the example rm -rf command of this post with the zsh "agnoster" theme

In the screenshot, there is a main prompt filled with blue with the current working directory and an undecorated prompt with a > sign indicating that we are continuing the line before.

As a third option, you could try to use external CLI tools that instead of removing the files, move them to the trash bin instead. One of the most popular is trash-cli that is available for macOS, Linux, and Windows. The author of that tool, Sindre Sorhus, also wrote a guide with other tricks to avoid accidents with rm.

I hope this post gives you more options for the next time you are tempted to use rm -rf and remember to be careful out there!