A single mistyped rm -rf on a server can cost an entire data disk. Rather than recovering after the fact, block high-risk commands like rm at the shell level in advance. This post builds two layers of protection with aliases: replace rm with a harmless message, then cover the cases where deletion is genuinely needed with “recycle bin + scheduled cleanup”, while also disabling mistake-prone, instant-firing commands like shutdown/reboot. Everything was tested live in a Rocky 9 container, and the process surfaced three pitfalls built into the scheme itself (nested interception, cleanup-cycle semantics, root’s default alias override) — each fixed one by one.

Lab Environment

  • A disposable Rocky Linux 9.3 container, bash 5.1.8, findutils 4.8.0;
  • Aliases expand only in interactive shells; the tests simulate an everyday terminal with a regular user + login shell (bash -l) — a non-login interactive shell does not load /etc/profile, so the aliases won’t take effect. This was the first detail tripped over during testing; before adopting the scheme, confirm which kind of shell you’re testing;
  • /etc/profile applies to login shells and affects all users; ~/.bashrc applies to every interactive shell and affects a single user. Use profile for machine-wide interception, bashrc for just yourself.

The core mechanism in one sentence: alias is the shell’s command substitution, happening at the enter-key interpretation stage — it guards against a human’s slippery finger; scripts and cron jobs are entirely beyond its reach.

Example 1: Intercepting rm

Edit /etc/profile, appending at the end:

1
2
######  rm prevent  ######
alias rm='echo can not use rm command'

Takes effect after re-login (or source /etc/profile). Tested: after typing rm the files are untouched and only a message prints:

Figure 1

There’s another pitfall dug up by testing here: on Rocky 9 the root user’s ~/.bashrc ships with alias rm='rm -i', which loads after /etc/profile at login and overrides the global interception back to interactive confirmation. If you want interception for root too, delete that line from root’s .bashrc, or simply operate as a regular account + sudo so the global alias genuinely lands on root.

Spend one minute verifying before rollout: in any user’s terminal run alias rm, which should print alias rm='echo can not use rm command'; then use type rmf to inspect the expansion chain — nested aliases become obvious at a glance. For rollout order, try a round in a test account’s .bashrc first, confirm daily interaction habits aren’t overly disturbed, then push to the machine-wide profile.

Example 2: The Safety-Delete rmf

Intercepting files isn’t the goal — production always has moments when deletion is necessary — so turn “delete” into “move + periodically clean”: move files slated for deletion into a recycle-bin directory (e.g. /data/recycleDir/) first, and keep a separate alias for confirmed deletion that retains interactive confirmation:

1
2
# Confirm the real deletion (keeps -i interactive confirmation)
alias rmf='rm -i'

Under an active interception, this form is broken. Tested: after alias rmf="rm -i", executing rmf expands recursively — rmf → rm -i → rm gets intercepted again by the first alias — so the final output is that harmless message and the files stay put — the safety delete cannot delete anything:

Figure 2

Fix: bypass alias expansion with an absolute path:

1
2
# Corrected form
alias rmf='/bin/rm -i'

After the fix, typing y really deletes and n keeps the file — interactive confirmation is back. The everyday “delete” action becomes mv /data/app/old.log /data/recycleDir/.

Example 3: Recycle Bin and Scheduled Cleanup

The companion cleanup script /data/scripts/del.sh periodically sweeps files that have sat in the recycle bin long enough. Create the recycle-bin directory first with adequate permissions:

1
2
mkdir -p /data/recycleDir
chmod 1777 /data/recycleDir

1777 matches /tmp’s mode: with the sticky bit, on a multi-user machine anyone can drop files but cannot delete others’, so the recycle bin won’t be wiped by mistake. The cleanup script itself:

1
2
3
4
5
6
#!/bin/sh
####################################################
####### Scheduled file deletion script #######
####################################################
location="/data/recycleDir/"
find $location -mmin +2880 -type f |xargs /bin/rm -f

First, two tested fixes about the script. One, aliases do not expand by default in scripts (non-interactive shells): writing | xargs rmf -f in the script was tested to fail with xargs: rmf: No such file or directory, silently killing the cleanup — so del.sh must spell out the full command /bin/rm -f.

Two, don’t copy old scripts’ -mtime +48 thinking in hours. Its semantics are not “48 hours” but “modification time more than 49 whole days ago” (find truncates to whole days before comparing). Tested with a file modified 49 hours ago: -mtime +48 does not catch it, while -mmin +2880 (2880 minutes = 48 hours) does; a file from 50 days ago is caught by both:

Figure 3

After choosing the unit that matches your business cycle, hook up scheduled execution with crontab (clean once daily at 1 a.m.):

1
0 1 * * * /bin/sh /data/scripts/del.sh

Before wiring up crontab, run sh /data/scripts/del.sh by hand once, then ls /data/recycleDir/ to confirm what should be cleaned is gone and what shouldn’t is still there — a cleanup script’s collateral damage costs exactly as much as a mistaken deletion. If something really did get deleted wrongly, there’s one last safety net: while the file just entered the recycle bin and the cleanup job hasn’t run, mv it back from /data/recycleDir/ to its original path to restore it.

Preventing Accidental Shutdown

Same idea: swap the shutdown-related commands for no-ops:

1
2
3
alias shutdown=''
alias init=''
alias reboot=''

These aliases also go in profile. A blanked alias expands to an empty command, so typing reboot quietly returns to the prompt and nothing happens; when a real shutdown/reboot is needed, comment out the corresponding alias line and source it, then re-login to the terminal to restore.

Emergency: Bypassing the Alias to Use Real rm

After interception goes live, there will occasionally be scenarios that genuinely require deletion. Three methods, ordered fastest first:

1
2
3
\rm file            # backslash skips the alias
command rm file # also bypasses the alias
/bin/rm file # absolute path, foolproof

These three methods also mean the interception is a formality — it guards against the “habitual slip” and cannot stop the “determined deleter”, which is why the recycle-bin layer is non-negotiable. One complementary idea is chattr +i, setting the immutable bit on key files so even root’s rm is refused by the kernel (filesystem-layer protection, covering a different segment than the alias layer); the container environment for this test lacked the relevant kernel capability so it wasn’t tested live — see the chattr(1) manual for real-machine usage.

Wrong Setups Compared

Mistake 1: expecting aliases to protect scripts. Cron scripts, ssh host 'rm ...', and other non-interactive invocations never touch aliases — the interception doesn’t exist there; the alias scheme only guards interactive slips.

Mistake 2: rmf='rm -i' nested under the interception. The expansion chain is rmf → rm -i → echo can not..., so confirmed deletion fails (tested in Example 2); you must write /bin/rm -i.

Mistake 3: using -mtime while thinking in hours. -mtime +48 fails to delete a file from 49 hours ago (tested in Example 3); to control by hours/minutes, write -mmin +2880.

Mistake 4: root’s default alias left unhandled. root’s ~/.bashrc ships with alias rm='rm -i', loaded after /etc/profile, and it quietly overrides the global interception (tested in Example 1); before rollout, check each user’s actual alias rm value.

Caveats

  • Aliases work only in interactive shells; scripts and remote one-shot commands are unaffected. They guard against slips, not permission holes.
  • sudo rm does not go through the current shell’s alias expansion, so the interception is equally ineffective for sudo calls.
  • The rm-disabling scheme must be paired with the recycle-bin cleanup script; otherwise /data/recycleDir/ only fills and never drains, bursting the disk sooner or later.
  • For the cleanup cycle, confirm whether you want -mmin +N (minutes) or -mtime +N (days) — the two differ by an order of magnitude in semantics.
  • Editing /etc/profile affects all users; verify the actual values of alias rm and alias rmf on a test machine with a regular user first, then roll out to production.

Summary

Back to the opening rm -rf: alias interception stops it from firing, rmf safety-delete (absolute-path form) keeps one confirmation, and the recycle bin plus scheduled cleanup covers the “really need to delete” cases — with three layers, the cost of a slip drops from “an entire data disk” to “a quick trip to the recycle bin”. Remember to carry the three tested fixes (/bin/rm -i, -mmin +2880, root’s default alias) into your own scripts; only then does the scheme truly close the loop.


This post was reconstructed from the author’s CSDN blog articles written between 2020 and 2024; it was originally published on CSDN.