First things first:

If you want to put a Trash can on your desktop, just take this Applescript application:

on open DroppedItems
	repeat with EachItem in DroppedItems
		tell application "Finder"
			delete DroppedItems
		end tell
	end repeat
end open

and put it on your desktop. If what you want is to create the equivalent of a "mv" to trash command for the command line, keep reading.

The executive summary

I made a perl script for making a "trash" command available at the command line. You can get it here, or copy and paste from below. Put it in your path (/usr/local/bin or ~/bin) and make it executable with chmod ogu+rx trash and you are ready to go.


For reference, this is the main script:


#!/usr/bin/perl -w
# Use and abuse as much as you want.
# Put it in /usr/local/bin/ or $HOME/bin/
# Daniel Cote a.k.a Novajo,
# Most recent version of this file available at
# Instead of deleting files, this script moves them to the appropriate trash folder.
# Trash folders are either private ($HOME/.Trash/) or public (/Volumes/name/.Trashes/
# and /.Trashes).  In the directory .Trashes/, each user has a trash folder named after
# its userid (501, 502, etc...).
# This script will simply stop if anything goes wrong. If by any chance it appears 
# that it might overwrite a file, it will let you know and ask for a confirmation.
# This script could be used as a replacement for rm.  The options passed as arguments
# are simply discarded.

# Usage: trash [options] file1|dir1 file2|dir2 file3|dir3 ... 
# You can use wildcards. To erase a directory, you are probably better off just naming
# the directory (e.g. trash Documents/) as opposed to trash Documents/* since the first
# case will actually keep the hierarchy and simply move Documents into the trash whereas
# the second one will move each item of the document folder into the trash individually.

if ( scalar @ARGV == 0) {
	die "Not enough argument.\nUsage: trash file1|dir1 file2|dir2 file3|dir3 ...\n\n";

my $MAX_INDEX = 10000;
my $uid = `id -u`;
my $workingdir = `pwd`;
my $home = $ENV{HOME};


if (! -e "$home/.trash_warning") {
	print "You must understand the possible problems here.\n";
	print "1) Unix and Mac OS X mix well if you know what you are doing.\n";
	print "2) Unix commands strip the resource fork from files they copy on HFS disks\n";
	print "3) UFS stores resource forks differently and this script will highly likely not work.\n\n";
	print "To get around the Unix-not-knowing-resource-forks problem, there are solutions:\n";
	print "1) use MvMac available from the Developer Tools to move files around the HFS-way\n";
	print "2) encode files as Macbinary, copy them and then decode them in the trash\n";
	print "3) forget about it and use Unix commands anyway\n\n";
	print "Option 1 is the best but only works if you have the developer tools installed.\n";
	print "Option 2 requires an extra program and actually copies the files.\n";
	print "Option 3 might be what you want and that's your decision.\n\n";
	`touch $home/.trash_warning`;

# We drop any option passed to command "trash"
# This allows to use trash as a replacement for rm
while ( $ARGV[0] =~ m|^-|i) {
	shift @ARGV;

my $mode;

if (-e "/Developer/Tools/MvMac" ) {
	$mode = $MOVE_MAC;
} elsif ( ! (`which macbinary` =~ m|Command not found.|i) ) {
	die "Macbinary not supported yet. Want to help? email to dccote\";
} else {
	# Comment out the following to use 'mv' instead of MvMac'.  You will corrupt mac files (but not flat files)
	die "Error: you do not have the developer tools installed.\nThe 'mv' command does not copy resource forks and will corrupt your files.  If you decide to use it anyway, comment out this warning to use 'mv' instead of 'MvMac'\n";
	$mode = $MOVE_UNIX;

foreach $fileordir (@ARGV) {

	my $fullpath;
	my $name;

	if ($fileordir =~ m|^/(.*)|) {
		$fullpath = $fileordir;
		$name = $1;
	} elsif (-f $fileordir || -d $fileordir) {
		$fullpath = "$workingdir/$fileordir";
		$name = $fileordir;
	} else {
		print "Skipping: not a file or a directory: $fileordir\n";

	my $trash;

	if ($fullpath =~ /$home/) {
		$trash = "$home/.Trash";
	} elsif ($fullpath =~ m|(/Volumes/.*?/)|) {
		$trash = "$1/.Trashes/$uid";
	} elsif ($uid == 0)  {
		$trash = "/private/root/.Trash";
	} else {
		$trash = "/.Trashes/$uid";

	if (! -e $trash) {
		print "Making trash directory $trash\n";
		`mkdir -p $trash`;
	} elsif (! -d $trash) {
		die "Error: $trash is not a directory";

	if (! -e $fullpath ) {
		die "Error getting full path to file: $fullpath does not exist\n";

	my $newname = $name;
	my $index = 2;

	while (-e "$trash/$newname") {
		$newname = "$name $index";

		if ( $index == $MAX_INDEX) {
			die "Error trying to rename file to avoid overwrite (too many files with the same name).\nYou should empty the trash.";

	print "$newname\n";
	# 1) $fullpath exists
	# 2) $trash exists and is a directory
	# 3) $trash/newname does not exist
	# So unless there is some screw-up with funny characters, the
        # -i option to mv should be useless, but who knows. MvMac will not overwrite.

	if ($mode eq $MOVE_MAC) {
		if (-e "$trash/$name" ) { 
			if (-d "$trash/$name") {
				print "Can't move and rename a directory\n";
			} else {
				if ( `/Developer/Tools/CpMac \"$trash/$name\" \"$trash/$newname\" 2>&1` ) {
					die "Error using CpMac\n";
				} else {
					if (`rm -f \"$trash/$name\" 2>&1`) {
						die "Error removing file\n";

		if (`/Developer/Tools/MvMac \"$fullpath\" \"$trash/\" 2>&1`) {
			die "Error using MvMac to move $fullpath to $trash/\n";
	} elsif ($mode eq $COPY_MACBINARY) {
		if (-d "$fullpath" ) {
			die "You must have the developer tools installed to move folders.\n";
		} else {
			if (`macbinary -e $fullpath 2>&1`) {
				die "Error using macbinary\n";
			`mv $fullpath.bin $trash/`;
			`macbinary -d $trash/$name.bin`;
	} elsif ($mode eq $MOVE_UNIX) {
		`mv "$fullpath" "$trash/$newname"`;	

Contact information

You can email me at for corrections, comments or questions.