Linux: Disabling rm and Other Dangerous Commands Against Accidental Deletion
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/profileapplies to login shells and affects all users;~/.bashrcapplies 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 | ###### rm prevent ###### |
Takes effect after re-login (or source /etc/profile). Tested: after typing rm the files are untouched and only a message prints:

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 | # Confirm the real deletion (keeps -i interactive confirmation) |
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:

Fix: bypass alias expansion with an absolute path:
1 | # Corrected form |
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 | mkdir -p /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 |
|
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:

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 | alias shutdown='' |
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 | \rm file # backslash skips the alias |
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 rmdoes 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 rmandalias rmfon 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.