A safer rm
I spend a lot of time in the terminal, which means deleting files and
directories with rm
. However, rm
isn't fussy about what it deletes and
doesn't believe in second chances. For interactive use, I think we can do a bit
better. In this article, I'll first discuss some safer alternatives to rm
and
then offer my current preferred solution.
Alternatives
One of the older (though still maintained!) efforts to improve the safety of
rm
is safe-rm, which is a simple wrapper
around rm
that refuses to delete a list of protected directories. By default
rm
will refuse to remove the root of the filesystem, /
, but that's it. In
contrast, safe-rm
refuses to remove a list of important system directories,
such as /bin
and /usr
. Additional directories can be protected by listing
them in a configuration file. safe-rm
was originally a Perl script but has
recently been rewritten in Rust, and is meant to be a drop-in replacement for
rm
.
The key feature of the subsequent alternatives is that files are moved to some trash directory instead of being immediately deleted, much like typical desktop GUI trash programs. This allows files deleted by mistake to be restored until the trash is permanently deleted later.
One example is shell-safe-rm,
which is a bash script that aims to be an almost-complete drop-in replacement
for rm
, except that it moves files to the directory ~/.Trash
. The tool does
not provide a built-in mechanism for restoring or emptying the trash, leaving
this for the user to do manually.
Another example is rip, which is written in
Rust. rip
is intended to be an intuitive and ergonomic alternative to rm
rather than a drop-in replacement. In particular, rip
doesn't bother with
-r
or other flag to remove directories. Like shell-safe-rm
, it moves
deleted files to a particular directory. By default, rip
uses a directory in
/tmp
, so that the deleted files are automatically cleaned up when the
computer reboots. I'm a little hesitant about this, in case my computer crashes
or otherwise reboots before I realize a file was removed by mistake; I want a
more predictable process for permanent deletion. Luckily, rip
allows the
destination for deleted files to be easily changed through environment
variables or a command line argument.
Finally, a rather attractive prospect is to follow the same specification as
common Unix desktop GUI trash programs, the XDG trash
spec. A
popular CLI tool that implements this spec is the Python tool
trash-cli. trash-cli
provides
scripts for all of the utilities expected from a trash program: move files to
trash, inspect them, restore them, or delete them permanently.
My preferred alternative
Ultimately, I want a tool with the following features:
- Move files to a trash directory rather than immediately deleting
permanently. This is typical of GUI trash programs and a main feature of
shell-safe-rm
,rip
, andtrash-cli
. - Confirmation before deleting multiple items (to avoid, say, an incorrect
glob). This is actually present in plain
rm
: the-i
flag prompts before removing each and every file;-I
prompts once before removing directories or more than three files. - Refuse to delete a protected list of directories, like
safe-rm
. - Files are automatically permanently deleted after having been in the trash
for
N
days.
I played around with my own implementation of (1) for a while, but eventually
decided to just wrap trash-cli
to get (1) and the rest of the XDG trash spec
for free. My wrapper just adds features (2)
and (3), both of which are quite simple to implement, on top of the trash-put
script from trash-cli
.
For feature (4), I initially had each call to my wrapper check for files older
than 7 days and delete them, which can cause a noticeable delay if there
is a lot to delete. A better solution is to set up a cron job to regularly
empty the trash automatically. One way to do this is to use the trash-empty
script from trash-cli
and a crontab entry like
@daily /path/to/trash-empty 30
which runs a daily job to remove files that have been in the trash for at least 30 days.
2023-08-06 update: the @daily
cron jobs are handled by anacron, which by
default runs as root and will not properly empty the user's local trash (see
this issue). One
option is to set up a non-root anacron; another is just to use a normal hourly
cron job like
0 * * * * /path/to/trash-empty 30
I'll also mention autotrash, which is a dedicated tool for automatically emptying the trash can. It can set up a systemd service to do so, or can be added as a cron job similar to the one above.
Summary
I discussed four safer versions of rm
:
After hacking on my own toy version for a little while, I ended up wrapping
trash-cli
and using a cron job to automatically remove old trash. If you have
an alternative (better?) solution to removing files in the CLI, I'd be happy to
hear about it by email or
Twitter.