CLI tool
andreafrancia/trash-cli avatar
andreafrancia/trash-cli

trash-cli: rm with an undo button, using the same trashcan your desktop uses

Command line interface to the freedesktop.org trashcan.

4,601 stars214 forksPythonGPL-2.0

At a glance

What is it?
A Python implementation of the freedesktop.org trash specification, so scripts can move files to the recycle bin instead of past it, and desktop apps can still restore them.
Who is it for?
The thing that makes trash-cli worth installing rather than writing a shell alias is interoperability: because it follows the freedesktop.org trash specification, a file trashed by a cron job shows up in the GNOME, KDE or XFCE file manager and can be restored from the desktop, which no rm wrapper can offer.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Five commands, and the file manager can see them

trash-cli moves files into the trashcan rather than unlinking them, and records the original path, the deletion date and the permissions for each entry. The point of that extra bookkeeping is interoperability: it uses the same trashcan used by KDE, GNOME and XFCE, so a file trashed from a shell script is visible and restorable from the desktop file manager afterwards.

The whole command set fits in one screen.

code
trash-put           trash files and directories.
trash-empty         empty the trashcan(s).
trash-list          list trashed files.
trash-restore       restore a trashed file.
trash-rm            remove individual files from the trashcan.

The daily use is deliberately small. `trash-put` is the replacement for `rm`, and unlike `rm` it does not need `-R` for directories, which the README flags as exactly why aliasing `rm` to it is a mistake. `trash-list` prints a deletion timestamp and the original path per line, which means it pipes into `grep` perfectly well.

code
$ trash-list | grep foo
2007-08-30 12:36:00 /home/andrea/foo
2007-08-30 12:39:41 /home/andrea/foo

That plain text output is the design decision that makes the tool scriptable. Nothing here needs a database or a daemon.

Restore is interactive, ranges and overwriting included

`trash-restore` with no argument lists the candidates as a numbered menu and prompts for a choice, which is the right default when you are restoring something you just trashed by accident.

code
$ trash-restore
0 2007-08-30 12:36:00 /home/andrea/foo
1 2007-08-30 12:39:41 /home/andrea/bar
What file to restore [0..1]: 0

It accepts ranges and lists, so restoring several entries at once is a matter of typing `0-2, 3` at the prompt. That is the detail that turns restore into something usable inside a script with piped input.

The collision case has its own flag. By default `trash-restore` will not clobber something already sitting at the original path, and `--overwrite` changes that, which the README demonstrates by trashing a file, recreating it with different contents and then restoring the old version over the top.

There is also selective removal inside the trash: `trash-rm` takes a pattern, and the README is specific that you need quotes so the shell does not expand it before the tool sees it.

code
$ trash-rm \*.o

Age based cleanup, which is the real reason to install it

`trash-empty` takes an optional number of days, and that is the feature most likely to end up in a crontab. The README walks through it with a concrete example: three files named today, yesterday and last_week, then `trash-empty 7` leaving the two recent ones, then `trash-empty 1` leaving one.

The README also gives the crontab line outright.

code
(crontab -l ; echo "@daily $(which trash-empty) 30") | crontab -

That is a slightly unusual idiom, piping the existing crontab plus one line back into `crontab`, but it is correct and it is the sort of thing people otherwise get wrong by hand-editing. Thirty days is the retention the README suggests for automatic cleanup.

Note what this does not do: nothing runs in the background. If you do not schedule `trash-empty`, files accumulate exactly as they would in the desktop trash, and the storage cost lands on the same filesystem as the originals.

Installation across five package managers and a cron caveat

The requirements are Python 3, with Python 2.7 also still working, and pipx described as optional but recommended because it installs into a clean environment.

code
pipx install trash-cli

Vanilla `pip install trash-cli` works too, and the README warns that the scripts land in `~/.local/bin`, which may need adding to your PATH. Distribution packages exist for Debian and Ubuntu (`apt install trash-cli`), Arch (`pacman -S trash-cli`), Fedora (`dnf install trash-cli`) and macOS via Homebrew, where the README also gives a PATH export for the Homebrew cellar path.

Installing from git means uninstalling first, and the README is unusually careful to list every package manager that might have left a copy behind.

code
$ [sudo] pip uninstall trash-cli # remove the previous version (with pip)
$ [sudo] apt-get remove trash-cli # remove the previous version (with apt)
$ [sudo] yum uninstall trash-cli # remove the previous version (with yum)

Shell completion is an extra rather than a default. The `[completion]` extra installs the scripts, and then a small loop generates bash, zsh and tcsh completions for all five commands into the right system directories.

code
cmds=(trash-empty trash-list trash-restore trash-put trash)
for cmd in ${cmds[@]}; do
  $cmd --print-completion bash | sudo tee /usr/share/bash-completion/completions/$cmd
done

That loop writes into `/usr/share/bash-completion/completions` and `/usr/share/zsh/site-functions`, so it wants root, which is normal on a workstation but worth noting on a managed machine.

Where files land, the .Trash directory, and the Btrfs gap

Anything trashed from the home partition goes to `~/.local/share/Trash/`, which is the freedesktop.org standard location. Files trashed from elsewhere land on a top level `.Trash` directory on that filesystem, and the README has a three line recipe for creating it with the sticky bit set.

code
sudo mkdir --parent /.Trash
sudo chmod a+rw /.Trash
sudo chmod +t /.Trash

The sticky bit is the security detail here. A world writable shared trash directory without it would let one user remove another user's trashed files.

The honest gap is Btrfs. The README states plainly that trash-cli does not support Btrfs volumes, and the reason given is that there was no system, time or knowledge to implement it. The suggested alternative is the `rmw` project. On a Btrfs system this is a hard limit rather than a rough edge, and one the author invites pull requests against.

For the Btrfs case the more interesting question is whether you need a trash at all. Btrfs snapshots and subvolume snapshots make a different answer possible, but that is outside what this tool does.

Why not alias rm, and what the last release hardened

The README contains an FAQ entry that amounts to advice against the thing most people want this tool for. Aliasing `rm` to `trash-put` seems compatible but is not, because the semantics differ, `rm` requires `-R` for directories while `trash-put` does not, and a script that inherits your alias will behave differently from what it expects. The author's compromise is an alias that simply refuses.

code
alias rm='echo "This is not the command you are looking for."; false'

Since bash aliases only apply in interactive shells, that trick does not leak into scripts, and prefixing with a backslash bypasses it when you genuinely want rm.

The most recent release, 0.26.9.14 on 2026-09-14, is unusually security focused, and several items were contributed by a reporter using the handle curious-rabbit. `trash-restore` now applies the same sticky bit and non-symlink checks when reading a shared trash directory that `trash-put` and `trash-list` already applied, instead of comparing user ids. It also refuses to restore a file outside the trash volume when a `.trashinfo` file contains a path traversal sequence in its `Path=` field. `trash-list` now sanitises control characters in filenames before printing, which blocks ANSI escape injection into a terminal. On the write side, `trash-put` no longer loops forever on an unwritable trash directory and rejects filenames shaped like options.

The last push to master was 2026-09-15, one day after that release, so the repository is being worked on now.

Editorial conclusion

The thing that makes trash-cli worth installing rather than writing a shell alias is interoperability: because it follows the freedesktop.org trash specification, a file trashed by a cron job shows up in the GNOME, KDE or XFCE file manager and can be restored from the desktop, which no rm wrapper can offer. The surface is deliberately five commands and the documentation answers the awkward questions people actually ask, including where files go and why aliasing rm is a bad idea. Two limits are worth checking against your setup before committing: Btrfs volumes are unsupported, and the shared top level .Trash directory has to be created by hand. Install with pipx, add the completion scripts if you live in a shell, and wire trash-empty into a daily crontab line to cap how long anything accumulates.

Frequently asked questions

How to empty trash from command line?

Use `trash-empty` from trash-cli, which empties every trashcan it can find for the current user. Pass a number of days to only remove entries older than that, so `trash-empty 7` deletes files trashed more than a week ago and leaves the rest. The README's crontab line runs `trash-empty 30` daily to cap how long anything accumulates.

How do I find my trash file?

Files trashed from your home partition live in `~/.local/share/Trash/`, the freedesktop.org standard location shared with GNOME, KDE and XFCE. Files trashed from other filesystems go to a top level `.Trash` directory on that filesystem, which you may have to create yourself with the sticky bit set. To search by name rather than browse, pipe `trash-list` through grep.

What is the Linux command to clean the trash?

`trash-empty` empties the trashcan outright, and `trash-empty <days>` removes only entries deleted more than that many days ago. For pattern based removal without emptying everything, `trash-rm` takes a quoted glob such as `trash-rm \*.o`. All three come from trash-cli, which works on Linux and macOS.

How do I empty the trash using the command line on a Mac?

trash-cli installs on macOS through Homebrew with `brew install trash-cli`, and the README notes you may need to add the Homebrew cellar path to your PATH. Once it is on the PATH, `trash-empty` behaves the same way it does on Linux. Be aware that Btrfs volumes are explicitly unsupported, which matters if the volume holding the trash is Btrfs.

Official sources

  1. andreafrancia/trash-cli on GitHub
  2. Issues
  3. License: GPL-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/andreafrancia-trash-cli.svg)](https://hysenlabs.com/projects/andreafrancia-trash-cli)