Safe Usage of /bin/rm

Those of you out there familiar with UNIX/Linux systems know that operating system tools and utilities that you have at your disposal are incredibly powerful. Arguably, this is one of the great strengths of UNIX/Linux systems. Basically, these systems give you unlimited control, and you can do anything you want without being bothered by pop-up windows, dialogs, etc.



I recently did a rm -rf * on the wrong directory.

That’s how all good stories start, right?

You always hear about rm -rf * horror stories. Everyone laughs and jokes about such things. It’s very amusing, unless you have just mistakenly done it to data rather important to you. But you have backups, right?

There are countless utilities and scripts out there to “make rm safe”. A quick Google search turned up several — mostly written in Perl. Unfortunately, I’m not very fluent in Perl. While the safe removal tools written in Perl were functional, they did not meet my more specific desires, and I did not feel comfortable modifying them to make them do so.

So, I wrote my own little script in Bash. Basically, I wanted my script to detect (and stop) any attempt to delete important directories directly (e.g. rm -rf /boot), as well as through globbing (e.g. rm -rf *). This is an important distinction as doing rm -rf / is just as bad as doing rm -rf * in the root directory. (Not all of the safe removal tools prevent the latter).

Basically, my script checks if the arguments to rm are absolute paths to important directories, or if globbed arguments match a certain number of keywords for important directories. Additionally, it throws in a short delay before even running. In my experience, I’ve hit executed rm -rf * only to immediately realize my error and hit ^C (Ctrl + c). But, of course, this is never faster enough. The short pause should give me a fighting chance at catching the mistake. (Of course, this may be a slight hindrance if you are doing something which uses rm in an automated fashion, like ./configure && make && make install. If that’s the case, recommend you comment out the sleep command.)


#!/bin/bash
sleep 2
args=( $@ )
preserve_paths=( / /bin /boot /dev /etc /home /initrd /lib /proc /root /sbin /sys /usr /usr/bin /usr/include /usr/lib /usr/local /usr/local/bin /usr/local/include /usr/local/sbin /usr/local/share /usr/sbin /usr/share /usr/src /var /opt )
preserve_keywords=( bin boot dev etc home initrd lib proc root sbin sys usr include local share src opt var )
for path in "${preserve_paths[@]}"
  do
    for arg in "${args[@]}"
    do
      if [[ $arg = $path ]];
      then
        echo "I refuse to delete: $path"
        echo "It seems too important. If you want to do it anyway, use /bin/rm"
        exit 100
      fi
    done
done
counter=0
matches=()
for keyword in "${preserve_keywords[@]}"
  do 
    for arg in "${args[@]}"
    do
      if [[ $arg = $keyword ]];
      then
        counter=$(($counter + 1))
        matches=( ${matches[@]} $keyword )
      fi
      if [[ $counter -gt 1 ]];
      then
        echo "I refuse to delete: ${matches[@]}"
        echo "They seem too important. If you want to do it anyway, use /bin/rm"
        exit 100
      fi
  done
done
/bin/rm "$@"

As you can see, it’s a simple script. I’m sure there are more efficient ways to accomplish the same thing — but I was able to throw this together quickly, and be confident it would stop me from repeating my error.

I’ve quickly installed this on my local computer and several other servers that I access regularly. To install it, I simply place it in /usr/local/bin, and make it executable:


sudo mv safe-rm.sh /usr/local/bin/rm
sudo chmod +x /usr/local/bin/rm