9

It's well known fact that extra space in env.variable can lead to deletion of / directory in bash script.

#!/bin/bash
...
rm -rf /$MYPATH

if $MYPATH contains values like " dir" or "dir /" it will lead to "rm -rf / dir" or "rm -rf dir /". And will result into "rm -rf /"

Are there any best practices to prevent this situation?

1
  • Solaris 10 will not let you do rm -rf / rm of / is not allowed
    – user9517
    Nov 9, 2010 at 14:50

10 Answers 10

12
alias rm='rm --preserve-root'

IIRC --preserve-root is the default in newer versions of coreutils.

1
  • 4
    In Bash, unless you do shopt -s expand_aliases aliases aren't expanded inside scripts. Also, aliasing rm is a bad idea - what if you're depending on the aliased behavior and the alias isn't there? Some versions of rm may not have --preserve-root. Nov 9, 2010 at 15:37
7

Always quote your arguments. Even when you know they are sane, it almost never hurts to quote them in scripts.

rm -rf "/$FOO" will not delete / if $FOO has a leading space, instead you'll just not delete anything. This does require the quotes to be present on the line with rm -rf, of course, not something like:

TODEL="/$FOO"
rm -rf $TODEL

If you do that, you'll be back in a whole load of trouble.

Also, I tend to think a good ol':

if [ -d "/$FOO" ] ; then
    ...
fi

(Or -e if it's just a file) is always a good idea before deleting anything.

2

First things first: have backups. :-)

But while I hack up those potentially dangerous scripts, I always first echo the dangerous lines, so I can see what would happen.

You can also add a file named -i to important directories, so in some situations rm would prompt while trying to remove those. Of course, if you do the deletion via some other method, such as Perl script or even with different rm parameters, that would not help.

It's also possible to set immutable flag to important files and dirs with chattr +i, but be careful with that one. That can bite you if you actually should remove files from some directory or modify the files ...

2

One way to avoid the issue would be to use an OS that prevents such a command to succeed by design as it is arguably non POSIX compliant. It was initiated by Solaris 10 (2005), followed by BSD then Gnu rm in 2006.

1

Sanity check your code first. Seriously, anything anyone will tell you will just be something equivalent to the check you should have done inside your code to check the value of $MYPATH. If the script is running interactively, you could remove the -f.

1

You can trim the MYPATH variable before executing the rm command. Just use echo:

MYPATH=`echo $MYPATH`
1
  • be aware that this will also convert any whitespace inside $MYPATH to single spaces, possibly corrupting it... (it applies the shell's word-splitting to the value, and then reconstructs it by appending the words, with spaces in between...)
    – r00t
    Dec 20, 2010 at 19:05
1

After an embarrassing incident many years ago on an Ultrix box, where I did as root a userdel -r sccs (or Ultrix equivalent, it's been a long time) without checking what the sccs user's home directory was beforehand, and the sccs user's $HOME turned out to be /, and the file system went away, I've avoided putting rm -rf $ANYTHING in scripts. You can check the variable until you're blue in the face, but I tend instead to print out a message like "if you're happy with the idea, you should now run sudo rm -rf $ANYTHING".

3
  • How about cleaning up the backups done by rsync?
    – igorp1024
    Nov 9, 2010 at 11:27
  • Sorry, I'm not sure I understand the question.
    – MadHatter
    Nov 9, 2010 at 12:56
  • I mean that telling user to run a "rm" command is not a good idea if you need automation (for example, deleting directory after rsync ant tar finished their job). But it's a good point for user's scripts.
    – igorp1024
    Nov 11, 2010 at 6:41
1

You could pass MYPATH through sed and check that what you put in is that same as what you get out

MYPATH1=`echo "$MYPATH" | sed -e 's|[ \t]\/[ \t]| |' -e 's|^/[ \t]| |' -e 's|[ \t]/$| |' `
if [ "$MYPATH" != "$MYPATH1" ]
then
    dosomething 
fi
0

You're doing a really funny thing here by taking what looks like a relative path and turning it into an absolute one. Treating the given path as the given path is a good start towards accomplishing what you want, but take other commenters' advice and definitely quote everything.

0

rm /* 😡

In my case, I ran rm /* without -rf option on a Debian 10 server.
A bit less critical, but not too far 😭.

System is still alive but many things are broken.

  • Mostly all commands are unavailable in my command line
  • I can not open a new SSH session
  • But, astonishing: my services are still running (docker, ssh, ...)

If I restart the server, I won't be able to login.
It would run in recovery mode.
panic mode

I've made all my tests on an equivalent case on a local VM to check my operations.

The point is that all symbolic links and files on / path are deleted.
Fortunately, all directories looks untouched. (that what has saved the situation 🙏)

Detailed road back to operational

Here are my detailed steps to get back to my machine with a runnable status.

  • Start server with an Ubuntu ISO to get a terminal from a bootable CD.

    Debian should work also.

  • Boot on CD.
    Do not install it, just use try mode.
    grub try ubuntu

    In my case, I'm using french keyboard ⌨ 🇫🇷 and I went through many keyboard issues.
    So, I decided to install openssh temporary server to run all following steps via SSH connection . For this, you can

    • open terminal (Ctrl+Alt+F3 / password: ubuntu)
      and type sudo apt-get install -y openssh-server
    • Customize password, with passwd command
    • Then, I was able to connect to ssh ubuntu@my-ip
      Get IP by typing ip a
    • and then, with sudo -s, I was back again in root mode in an efficient way
  • Mount harddrive on /debian path.

    mkdir /debian
    mount /dev/sda1 /debian
    
  • Here is the tricky part: you have to compare your /debian subdirs with a equivalent system.
    On basic Debian 10.11 setup, here is default structure : default debian structure.
    In my case, I started with following

    • main directories : bin, lib, lib32, lib64, libx32 and sbin
    • vmlinux and initrd.img files

    For this,

    • go to /debian dir with cd /debian
    • then, use syntax ln -s ./usr/bin ./bin.
      Relative path are required.

    Have a look on script section below, it can help

    From this, first reboot try. 😨 🤞 (hardware reboot, with CD removal).
    But, fortunately, it starts again in normal mode 🎉

I've declined this on 3 test VM before running it in production. I've tested Debian 11, Debian 10.11 & Ubuntu If your on a virtual system, you may run a snapshot on broken server before running these steps, in order to get back if things go worst.

I'm running tests for a few hours now, but it looks 100% operational and back to previous settings.
You can breath again 🫁

Script

For fast deployment of these steps, you can use my script on linux-rm-all-recovering.sh.

wget https://media.gtnapp.com/it/linux-rm-all-recovering.sh
bash linux-rm-all-recovering.sh

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .