192

I do a lot of terminal work, and today had the experience of typing

rm fileInQuestion.txt

Before finding out that I did actually need fileInQuestion.txt. If I'd deleted in the GUI then I would have just gotten it out of the Trash. I'd like to know if it's possible to overload 'rm' in the Terminal in such a way that it sends the file/files to the Trash on the way out.

Cajunluke
  • 17,704
Joe
  • 5,039
  • 1
    There are programs that can recover deleted files (as long as those sectors on the hard drive are not overwritten in the mean time!). When this happens, you should use one of those... – iconoclast Jun 06 '14 at 13:54
  • Also see http://askubuntu.com/q/468721/250556 for googlers looking for ubuntu/debian/linux file trashing. – ThorSummoner Jul 16 '15 at 19:48

15 Answers15

167

The trash command line tool can be installed via brew install trash or port install trash.

It allows you to restore trashed files via command line or the Finder.

gagarine
  • 123
Paul Wenzel
  • 1,789
131

I wouldn't advise aliasing rm to mv as you might get in the habit of rm not permanently deleting files and then run into issues on other computers or under other user accounts when it does permanently delete.

I wrote a set of bash scripts that add more Mac OS X-like command line tools (in addition to a number of the built-in ones like open, pbcopy, pbpaste, etc.), most importantly trash. My version of trash will do all the correct things that aliasing rm won't (and hopefully nothing bad, but I've been using it on my own Macs for a few years now without any lost data), including: renaming the file like Finder does if a file with the same name already exists, putting files in the correct Trash folder on external volumes; it also has some added niceties, like: it attempts to use AppleScript when available so you get the nice trash sound and such (but doesn't require it so you can still use it via SSH when no user is logged in), it can give you Trash size across all volumes.

You can grab my tools-osx suite from my site or the latest and greatest version from the GitHub repository.

There's also a trash command developed by Ali Rantakari, but I haven't tested that one myself.

morgant
  • 1,475
  • 5
    Excellent answer, much better than mine! – user1256923 May 09 '12 at 15:25
  • 33
    I've used trash by Rantakari for quite a while and can really vouch for it. It is compiled Objective-C and cleverly uses standard filesystem APIs and, should it fail (ie. user doesn't have sufficient rights), it calls Finder to trash the files (effectively prompting for authentication). You can read more info of trash from hasseg.org/blog. – Jari Keinänen May 09 '12 at 16:39
  • 35
    trash is also available via the brew package manager. just use: brew install trash. – Landon Kuhn Jan 22 '13 at 23:03
  • 10
    @landon9720 Good point. And, to clarify for others, the trash command available through Homebrew is Alai Rantakari's (see the brew formula). – morgant Jan 24 '13 at 00:36
  • 2
    @landon9720 love me some homebrew trash! – Randy L Feb 06 '14 at 16:19
  • I just saw your git page: 336 lines of bash code to move a file to the trash? ;) – CousinCocaine Aug 01 '14 at 14:22
  • 1
    @CousinCocaine Haha, yes, it could use some consolidation after all these years. That said, it's well commented and supports a lot of functionality (trash across multiple volumes, moving to trash directly or via AppleScript, empty & secure empty, etc.), so it wouldn't be particularly short anyway. :) – morgant Aug 01 '14 at 20:42
  • @morgant, indeed. It looks clean and very complete. Nice. – CousinCocaine Aug 02 '14 at 10:24
  • 1
    I have been using Ali Rantakari's trash command for years, and I like it. But I upgraded to Yosemite and it stopped working. I get an error saying -bash: /usr/bin/trash: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby: bad interpreter: No such file or directory. Does anyone know how to fix this? – Elias Zamaria Jan 12 '15 at 19:45
  • 3
    I was able to fix the error by running brew remove trash and then brew install trash. – Elias Zamaria Jan 16 '15 at 06:41
  • are link-only answers actually acceptable here? They aren't on stack overflow. There's nothing here actually answering the question other than a link they may go bad at any time. – xaxxon Feb 24 '18 at 23:45
  • @xaxxon I appreciate your question. As the author of this answer, my original goal was to answer the question in the first paragraph (advising against aliasing rm), then provide alternate solutions. Others certainly seem to feel that my answer most thoroughly answered the question, but I’m happy to expand upon my first paragraph if you feel it would be helpful. – morgant Feb 25 '18 at 03:29
  • @morgant Advising against something is a comment, not an answer. – xaxxon Feb 25 '18 at 05:35
  • site is down. rip. – Carter Pape Jul 26 '18 at 16:25
  • @CarterPape I know why you had to put 'rip' after 'site is down', smart choice :)) – Daniel B Apr 14 '20 at 08:19
  • The site is back up, though most of the updates have been in the GitHub repo. – morgant Dec 15 '20 at 14:58
  • 2
    Many comments here talk about the alternative trash command installed by brew install trash. To be clear, that trash does not by default use the Finder anymore, so "Put Back" will not work unless you use a flag. Even when you do use the flag, it often errors (which is why the default was changed). So, morgant's script is of higher quality. It can be installed using zinit with this command zinit wait'1' lucid light-mode as"program" pick"src/trash" for morgant/tools-osx – Klas Mellbourn Mar 28 '21 at 19:04
33

I have an executable called rem somewhere in my $PATH with the following contents:

EDIT: code below is a revised and improved version in collaboration with Dave Abrahams:

#!/usr/bin/env python3
import os
import sys
import subprocess

if len(sys.argv) > 1: files = [] for arg in sys.argv[1:]: if os.path.exists(arg): p = os.path.abspath(arg).replace('\', '\\').replace('"', '\"') files.append('the POSIX file "' + p + '"') else: sys.stderr.write( "%s: %s: No such file or directory\n" % (sys.argv[0], arg)) if len(files) > 0: cmd = ['osascript', '-e', 'tell app "Finder" to move {' + ', '.join(files) + '} to trash'] r = subprocess.call(cmd, stdout=open(os.devnull, 'w')) sys.exit(r if len(files) == len(sys.argv[1:]) else 1) else: sys.stderr.write( 'usage: %s file(s)\n' ' move file(s) to Trash\n' % os.path.basename(sys.argv[0])) sys.exit(64) # matches what rm does on my system

It behaves in exactly the same way as deleting from the Finder. (See blog post here.)

  • 6
    This should have more points IMO. Because it is AppleScript it uses the actual trash process to move the file to trash, which means it will behave exactly the same. Shame a one-liner was not provided however. – mxcl Mar 02 '16 at 18:07
  • Well, someone downvoted it on Oct 31 '15 at 10:26 - at precisely the same time that someone else was posting an answer to this question! – Anthony Smith Mar 03 '16 at 09:58
  • 3
    This answer is an object lesson in how to do things right. It works with shell wildcards and it works through the Finder. It doesn't reinvent any wheels, and it's about as simple as can be. I put it in my ~/bin, named it trash, and it worked the first time—enabling me to trash a whole lot of files way more efficiently than if I had to click on them in Finder windows. Much gratitude. I hope you'll post more answers! – Ben Kovitz Apr 05 '17 at 14:50
  • Unfortunately, the script does not handle 'characters in file names. I was trying to use it with one of those "(John Doe's conflict copy)" files that Dropbox sometimes creates, when it failed on me. – NSSynapse May 28 '18 at 09:11
  • 3
    This version handles quotes. It changes error handling behavior a bit, which in the version above seems to be not-very-carefully-worked-out (errors to stdout instead of stderr, no exit code) but the error you get from osascript when the file doesn't exist is not beautiful so you might want to tune that part. – Dave Abrahams Sep 06 '18 at 21:46
  • 3
    Nice job. This version incorporates those changes, and also (1) tries to imitate the shell command rm as much as possible in terms of displaying messages when files don't exist, and (2) collates everything into a single osascript call. – Anthony Smith Sep 10 '18 at 13:21
  • 4
    One-upping: https://gist.github.com/dabrahams/14fedc316441c350b382528ea64bc09c – Dave Abrahams Sep 10 '18 at 23:28
  • @DaveAbrahams: This is clearly THE answer :) To be "unixly correct" (UC),the script should be installed as /usr/local/bin (or, /usr/local/share if you prefer). Name the file /usr/local/bin/trash , and: sudo chmod a+rx usr/local/bin/trash. – Seamus Apr 09 '19 at 23:46
21

Use the terminal command osascript, the AppleScript interpreter.

osascript -e "tell application \"Finder\" to delete POSIX file \"${PWD}/${InputFile}\""

This tells AppleScript to tell Finder to send the file to trash.

PWD is needed for relative file paths, as AppleScript does not handle that well.

cde
  • 661
9

a modern approach using swift

https://github.com/reklis/recycle

//
// main.swift
// recycle
//
// usage: recycle <files or directories to throw out>
//

import Foundation
import AppKit

var args = NSProcessInfo.processInfo().arguments
args.removeAtIndex(0) // first item in list is the program itself

var w = NSWorkspace.sharedWorkspace()
var fm = NSFileManager.defaultManager()

for arg in args {
    let path = arg.stringByStandardizingPath;
    let file = path.lastPathComponent
    let source = path.stringByDeletingLastPathComponent

    w.performFileOperation(NSWorkspaceRecycleOperation,
        source:source,
        destination: "",
        files: [file],
        tag: nil)
}
nohillside
  • 100,768
slf
  • 399
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – jherran Mar 20 '15 at 05:07
  • @jherran thanks for the reminder, I added a code segment – slf Mar 20 '15 at 11:43
  • This is my preferred answer because it uses the same underlying logic and APIs as the GUI. – Jason R. Coombs Oct 07 '15 at 14:16
  • 1
    Now if only there were a Python version that avoided the need for a 3MB binary to make one system call. – Jason R. Coombs Oct 07 '15 at 14:18
3

Here's a pretty trivial one-line solution to add to your bash profile. Note that it will overwrite something with the same name in the trash already.

trash() { mv -fv "$@" ~/.Trash/ ; }

Usage:

• ~/Desktop $$$ touch a b c
• ~/Desktop $$$ ls
a b c
• ~/Desktop $$$ trash a b c
a -> /Users/ryan.tuck/.Trash/a
b -> /Users/ryan.tuck/.Trash/b
c -> /Users/ryan.tuck/.Trash/c
• ~/Desktop $$$ ls
• ~/Desktop $$$
ryantuck
  • 209
3

This is an improvement to the answers given by Antony Smith and cde.

The problem with embedding a filename in a string that is passed to osascript via -e is that, on modern Unix-like systems, filenames can contain quotes, to the effect that parts of the filename could be run as AppleScript.

A safer way to do this is to read the filename from an environment variable:

trash() (
    : "${1:?}"
    case $1 in
        (/*) FNAME="$1" ;;
        (*)  FNAME="$(pwd)/$1"
    esac
    export FNAME
    exec osascript <<-EOF >/dev/null
        set fName to system attribute "FNAME"
        tell application "Finder" to delete my (POSIX file fName)
    EOF
)

If you add this function to your .bashrc or .zshrc, you can call trash from the command line. EDIT: Though turning this into a function that is actually useful requires more work, of course.

  • Nice. I forgot that hfs + and apfs allow " and other special characters in filename. But if you use quotes in filenames, there's a 8th level of "hell". – cde Mar 08 '23 at 08:51
3

I found a pretty nice code that can be added at the end of user's batch profile and causes rm to move the files to the trash each time it is run.

nano ~/.bash_profile

#... append at the end
function rm () {
  local path
  for path in "$@"; do
    # ignore any arguments
    if [[ "$path" = -* ]]; then :
    else
      # remove trailing slash
      local mindtrailingslash=${path%/}
      # remove preceding directory path
      local dst=${mindtrailingslash##*/}
      # append the time if necessary
      while [ -e ~/.Trash/"$dst" ]; do
        dst="`expr "$dst" : '\(.*\)\.[^.]*'` `date +%H-%M-%S`.`expr "$dst" : '.*\.\([^.]*\)'`"
      done
      mv "$path" ~/.Trash/"$dst"
    fi
  done
}

source: http://hints.macworld.com/article.php?story=20080224175659423

mach
  • 889
rraallvv
  • 2,290
  • 3
    This is v clever but I still wouldn't recommend this. It's clever because its in the bash profile for the user and so only the user can execute this version of the function by typing it in, scripts that rely on rm will still call the original. But I wouldn't recommend doing this because the user will get used to rm acting in this way when it doesn't on other machines. I'm going to use this but rename the function "trash" – Matt Parkins Feb 21 '14 at 10:06
  • I am using the same function with two changes: ① I corrected a bug in the function that causes any folder to be renamed to the timestamp, if it has been tab-completed to include a trailing slash: This requires the following changes: Replacing the line local dst=${path##*/} with local dst=${mindtrailingslash##*/} and inserting another line just before that one that says local mindtrailingslash=${path%/}. ② I use the name function rr. Like this, it does not interefere with plain rm, but the name is similarly short and fast to type (any other name would do). – mach Apr 07 '15 at 14:56
  • Change the name of rm() to rmt() - This way you can still use the rm command when you want. I think of rmt in my head as remove to trash :D – James111 Jan 29 '16 at 06:09
2

There are two utilities installable via Homebrew that can accomplish this:

  1. trash

    This is a small command-line program for OS X that moves files or folders to the trash. The USP of this command is that enables to easily restore the files. A command to trash files/folders is no use if you can't restore files/folders after trashing them. From the command's website:

By default, trash asks Finder to move the specified files/folders to the trash instead of calling the system API to do this because of the "put back" feature that only works when trashing files through Finder.


-F

Ask Finder to move the files to the trash, instead of using the system API. This is slower, but it utilizes Finder's UI (e.g. sounds) and ensures that the "put back" feature works.

-l

List items currently in the trash. If this argument is used, no files need to be specified.

-e

Empty the trash. trash asks for confirmation before executing this action. If this argument is used, no files need to be specified.

To install trash run the following in Terminal:

brew install trash.


  1. rmtrash

    A command line tool that move files to the trash. From the command's man page:

This command moves files to the trash rather than removing them totally from the file system. Very useful if you decide you want that file after all...


-u USERNAME

an optional argument. This will move the file to the specified user's trash. Note that you need sufficient privileges to accomplish this.

To install rmtrash run the following in Terminal:

brew install rmtrash.

Nimesh Neema
  • 51,809
1

While it is possible to make rm move files to Trash instead of removing them, I would advise against bringing the mindset of the safety net of graphical user interfaces to the UNIX shell. There are many ways to do serious damage using the terminal. The best advise IMHO is to simply think twice before hitting the enter key in a shell window.

If you want rm to remind you that you are about to delete a file consider using the following alias (for /bin/bash put this line in .bashrc in your home directory):

alias rm "rm -i"

This will make rm request confirmation before attempting to remove each file.

If you have TimeMachine running (I hope so!) you can always get your file from backup. This way you can lose at most one hour of work. Which is bad enough, of course. So think again before pressing that enter key!

grg
  • 201,078
  • 1
    Don't do it! Installer scripts may use rm and hang with the alias rm -i. – Old Pro May 09 '12 at 20:43
  • 3
    @OldPro: .bashrc is only executed if the shell is interactive. Check the man page! – Mackie Messer May 09 '12 at 21:07
  • 1
    i would advise against removing safety nets, or admonishing others to do so. – Randy L Feb 06 '14 at 16:18
  • I'll go against the naysayers and say that I use this trick to great effect, but it definitely should be only done knowing the pitfalls. Two not mentioned are that it trains your brain that rm is safer than it is which will get your in trouble on other systems. Also, if you use -f, -i is completely ignored. That said, this is a good safety net. A better one is to never use rm in favor of an rmi alias (alias rmi="rm -i") or the excellent trash utility. Also, I'll add that everyone should have put [ -f ~/.bashrc ] && source ~/.bashrc in their .bash_profile by now. – mattmc3 Jan 06 '18 at 14:10
0

Check out trash-cli. It works cross-platform, no trash sound, and supports Put Back.

You can install it with (requires Node.js):

$ npm install --global trash-cli

Alternatively, if you don't want to use Node.js, you can install the native binary osx-trash manually.

0

Properly trashing stuff (so that it is definitely recoverable) is trickier than simply a mv to ~/.Trash.

osx-trash might be what you're looking for. (Caveat emptor - I haven't tried it, and cannot vouch for how safe it is.)

Nix
  • 987
-1

I have simply put this script

#!/bin/bash
application=$(basename "$0")
if [ "$#" == 0 ]; then
    echo "Usage: $application path [paths...]"
    exit 1
fi
trashdir="/Users/${USER}/.Trash"
while (( "$#" )); do
    if [ -e "$1" ]; then
        src=$(basename "$1")
        dst=$src
        while [ -e "$trashdir/$dst" ]; do
            dst=$src+`date +%H-%M-%S`
        done
        mv -f "$1" "$trashdir/$dst"
    else
        echo "$1" does not exist.
    fi
    shift
done

in ~/bin/trash, made it excutable chmod +x ~/bin/trash, and added the following line to ~/.bash_profile

PATH=$PATH:$HOME/bin

Then one can use it as

$ trash broken.js olddir cleanup.*
jpsecher
  • 959
-1

In your .bashrc (or wherever you keep the parameters for your shell), try adding an alias that changes the behaviour of rm to moving stuff to ~/.Trash, as in:

alias rm='move/to/.Trash'

This alias if far from trivial to implement (at least for me) though, because the use of mv (the prime candidate to use for this job) is

mv file where

so having an alias that puts the 'where' part in front of the file to be moved might be pretty sketchy. I'll look into it an might get more substantial advice.

EDIT: I just tried to add the following to my .bashrc, and it works:

function trash { mv "$@" ~/.Trash ; }

It is much more primitive than other suggestions, but you avoid installing new stuff.

nohillside
  • 100,768
user1256923
  • 3,610
  • 3
    Aliasing rm to anything is dangerous because it may break installer scripts. I learned this the hard way when I aliased rm to rm -i and had installs hang. – Old Pro May 09 '12 at 20:42
  • @Old Pro, that's the alias I use for rm in all my computers, from day 0, and I never had issues because of it. Care to give an example? – user1256923 May 09 '12 at 20:56
  • I don't recall the specific application I was installing, but I do recall that installing it (probably an upgrade) hung and it took me about a month to figure out it was because one install script used rm instead of rm -f and the script was hung waiting for confirmation that it was OK to delete some file. Removing the alias from my .bashrc allowed the install to complete and I've refrained from using that alias on OS X ever since, even though I still use it on every other Unix system. I'm not sure what your alias will do with rm -f someFile. – Old Pro May 09 '12 at 21:04
  • 3
    @OldPro: .bashrc is only used for interactive shells. If you run ./install.sh a new process is started and the alias will not be active. However, if you run . install.sh your current process will execute the installer and the alias is active. RTFM... – Mackie Messer May 09 '12 at 21:21
  • I was using a standard installer started by double-clicking in the Finder. I determined the process that was stuck was rm -i by using ps to see the command line and I fixed the problem by removing the rm alias. YMMV – Old Pro May 09 '12 at 21:28
  • 3
    Then the installer was running an interactive shell and therefore was seriously broken. One broken installer is hardly enough reason to advise against shell aliases in general. YMMV – Mackie Messer May 09 '12 at 21:51
  • 3
    However, it is good advice to not alias the rm command, as morgant said, because you'll become comfortable with rm not actually removing files and then might accidentally delete something on a system where no such alias has been added. Also, moving a file to the trash is not as simple as just mv {} ~/.Trash. If the file is on a separate volume, for example, this will copy the file to your home directory and delete the original. – Josh May 30 '13 at 21:46
  • I also use this short function in my .bashrc; however, you need to this change to function trash { mv "$@" ~/.Trash ; } to avoid it breaking on folder names with spaces in it. – user86559 Aug 01 '14 at 14:09
-3

A simple function could let you trash files by moving them to the user's .Trash folder:

trash() {
   mv $1 ~/.Trash
}
bmike
  • 235,889
  • ? why a negative vote? this would be my thought as well.. – CousinCocaine Aug 01 '14 at 14:25
  • You probably need $* instead of $1, but then you would end up with the same answer as mv "$@" ~/.Trash. Why people vote it negatively, check the comments from the other answer. – kenorb May 06 '15 at 21:36
  • Be careful with this alias; this works if you only have one partition or drive. If you're working on a second volume or network share, this will actually copy the file to your home directory's trash, which is probably not what you wanted to do! – jmk May 28 '15 at 10:46