Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

Use OS X's trash in a Finder-like way from Terminal UNIX
I've written a few bash functions to interact with the normal Mac OS X trash can. This is actually slightly more complicated that it sounds at first, since the trash can is not just a folder somewhere. Each volume has its own trash, and your home folder has a trash can. Finder uses your home trash for any files on the boot volume, unless you have FileVault on.

Start by adding the following code (note you can check newer versions of the code on the mactrash SourceForge page) to your bash startup file (.bashrc or .profile).
#!/bin/bash -c 'echo This file is meant to be sourced.'

alias rm='del'
  # make rm(1) safe.
  # Remove or comment-out this line to return to normal rm(1) functionality.

function del ()
{
  if declare -F trash >/dev/null
  then
    trash "$@"
  else
    command rm -i "$@"
  fi
}

function trash ()
{
  local F
  local HOME_DEVICE="$(stat -f %Sd "$HOME")"
  local TRASHCAN=~/.Trash
    # Set this in advance _outside_ the loop below
  for F in "$@"
  do
    if ! test -e "$F"
    then
      echo "No such file or directory: $F" 1>&2
      return 4
    fi
    
    local DEVICE="$(stat -f %Sd "$F")"
    
    if [ x"$DEVICE" == x"" ] || [ x"$DEVICE" == x"???" ]
    then
      echo "Can't locate trash for ${F}." 1>&2
      return 3
    fi
    
    if [ x"$DEVICE" != x"$HOME_DEVICE" ]
    then
      TRASHCAN="$(trashOnDevice "$DEVICE")"
    fi
    
    if [ ! -d "${TRASHCAN}" ]
    then
      command rm -f "${TRASHCAN}"
      if ! mkdir -m 700 "${TRASHCAN}"
      then
        echo "$TRASHCAN is inaccessible at this time." | sed 's;'"$HOME"';~;g' 1>&2
        return 1
      fi
    fi
    
    local FinT="$(basename "$F")"
    
    if [ -e "${TRASHCAN}/${FinT}" ]
    then
      FinT="$(date) ${FinT}"
    fi
    
    if ! mv -vn "$F" "${TRASHCAN}/${FinT}"
    then
      echo "Unable to move $F to the trash." 1>&2
      return 2
    fi
  done
  
  local TRASHSIZE="$(du -hs "${TRASHCAN}" 2>/dev/null | cut -f 1)"
  local TRASHCANloc="$(dirname "$TRASHCAN" | sed 's;^/Volumes/\(.*\)/.Trashes;\1;g' | sed 's;'"$HOME"';~;g' | sed 's;^/.Trashes;/;g')"
  echo "${TRASHSIZE:-  0B} in trash on $TRASHCANloc."
}

function emptytrash ()
{
  local TMPIFS="$IFS"
  IFS='
'
  local MOUNTS=( $(mount | sed -n 's:/dev/.* on \(.*\) (.*):\1:p') )
  local TRASHCANs=( "${HOME}/.Trash" $(IFS="$TMPIFS";for i in `seq 0 $(( ${#MOUNTS[@]} - 1 ))`; do echo "${MOUNTS[$i]}/.Trashes/$(id -u)"; done) )
  IFS="$TMPIFS"
  unset TMPIFS

  local TRASH_SIZE
  TRASH_SIZE="$( (for i in "${TRASHCANs[@]}"; do ls "$i"/; done) 2>/dev/null | wc -w)"
  if [ "$TRASH_SIZE" -gt 0 ]
  then 
    echo -n "Emptying trash"
    for i in "${TRASHCANs[@]}"
    do 
      tput smcup
      pushd "$i" 2>/dev/null && {
        srm -frsvz . 2>/dev/null ; popd ;
      }
      tput rmcup
      echo -n .
    done
    local DONE=
    [ `ls "${HOME}/.Trash" | wc -w` == 0 ] && DONE="Done."
    echo "$DONE"
  else 
    echo "Trash is empty."
  fi
}

function trashOnDevice ()
{
  local DEVICE="$1"
  local MOUNT="$(mount | sed -n 's:/dev/'"$DEVICE"' on \(.*\) (.*):\1:p')"
  
  if [ x"$MOUNT" == x"" ] || [ x"$MOUNT" == x"???" ]
  then
    # If no mount point is found, then don't return the path to root!
    return 1
  elif [ x"$MOUNT" == x"/" ]
  then
    # Encourage the resulting path to _not_ start with two slashes
    MOUNT=""
  fi
  
  echo "$MOUNT/.Trashes/$UID"
}

# Usage : seq n m [i]
# echo all integers between n and m using a skip or increment of i
function seq ()
{
  [ "$1" ] || [ "$2" ] || return 1
  
  local x=$1;
  local y=$2;
  local i=${3:-1};
  local seperator="${4:- }"
  while [ $x -le $y ]
  do
    echo -n $x"${seperator}";
    x=$(( $x + $i ));
  done
  echo
}
Once you've added the above code to ~/.bashrc, you'll find that Terminal will now accept three new commands: del, trash, and emptytrash.

del: This is just another name for trash.

trash: If you have a file on your Desktop that you want to move to the trash, you can type trash ~/Desktop/the_file_to_trash.doc, and the file will disappear from your Dekstop and appear in your Trash -- the very same trash that you see in your Dock. You can specify as many files as you like at the same time.

emptytrash: If you enter emptytrash in Terminal, every file in your trash will be securely erased in roughly the same fashion that Finder uses with its 'Secure Empty Trash' command. This takes a long time. If you want to insecurely empty your trash, then simply use the Finder. Perhaps I'll update this code (at some point in the future) to allow insecure erase, too.

Additionally, this code as provided will also disable rm by pointing it to del (which in turn points to trash). I use this to prevent myself from accidentally deleting important things, as well as to ensure that other scripts or code or whatever don't delete important things either.

Technical Details

Finder's trash is actually a combination of several different folders, not just one.

Your home folder has a folder named .Trash which stores all the trash that comes from the same volume as your home folder (usually your boot disk). So, in a default configuration with no extra hard drives, all files that get put in the trash in Finder will end up in ~/.Trash. However, nothing is ever so simple.

If you connect a hard drive to your Mac, and move a file on that hard drive into the trash, that file is not moved into your home folder's trash. Instead, it is moved into a trash on that hard drive (but only visible to your user account). However, you still see that file in the Finder's trash, because Finder combines all of the trashes it can find into one magical folder.

The trash function here provided uses stat to check which hard drive each file lives on, and then uses that information to figure out where to put the file. Since FileVault is implemented as a disk image, this same code also serves to ensure that trashing files inside and outside of a FileVaulted home folder also works as expected. (Likewise, it also checks to see if a file already exists in that trash with the same name, and re-names the more-recent file to prevent accidental name collision, which would result in data loss.)

The emptytrash function doesn't need to be as clever. It just srm's everything in every trash it can find.

[robg adds: I tested these, and they work well. Note that we've run many hints on using the trash from Terminal, but I think this is the first one that properly handles files on other volumes.]
    •    
  • Currently 3.40 / 5
  You rated: 5 / 5 (15 votes cast)
 
[12,341 views]  

Use OS X's trash in a Finder-like way from Terminal | 28 comments | Create New Account
Click here to return to the 'Use OS X's trash in a Finder-like way from Terminal' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
Use OS X's trash in a Finder-like way from Terminal
Authored by: Sesquipedalian on Apr 22, '10 08:58:19AM

This code looks like it will be very helpful.

However, instead of putting it into .bashrc (or .bashprofile or .profile), I plan to turn it into some executable scripts and save them in /usr/local/bin. That way they will be available to all users and to any type of script that I might want to write.



[ Reply to This | # ]
Separated scripts, instead of functions
Authored by: GaelicWizard on Apr 22, '10 04:50:54PM
I've been meaning to do that for a while, especially since these functions are basically self-contained. Their only dependencies are on seq(). I'll update the SourceForge page with that at some point.

[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: Sesquipedalian on Apr 23, '10 10:50:32AM
It occurred to me after posting my previous comment that there is a much simpler way of accomplishing the goals of this hint. Moreover, this alternative method avoids the possible problems that others have posted about here, since it works not by emulating the process Finder uses when trashing things, but by actually having the Finder itself do the job.

To send files to the Trash using the command line, save the following code in a file on your Desktop called trash, make it executable (chmod +x ~/Desktop/trash) and store it somewhere on your bash $PATH (I recommend /usr/local/bin)

#!/bin/bash
for arg in "$@"
do
if [ -e "$PWD/$arg" ]
then
osascript -e "tell application \"Finder\" to delete POSIX file \"$PWD/$arg\"" &>/dev/null
elif [ -e "$arg" ]
then
osascript -e "tell application \"Finder\" to delete POSIX file \"$arg\"" &>/dev/null
else
echo File not found
fi
done

To empty the trash, this code will work nicely:
#!/bin/bash
osascript -e "tell application \"Finder\" to empty trash"

To empty the trash securely, use this:

#!/bin/bash
osascript -e "tell application \"Finder\" to empty trash with security"

EDIT: Updated the trash script's code to handle relative path arguments.

Edited on Apr 23, '10 11:31:41AM by Sesquipedalian


[ Reply to This | # ]
Just script the Finder via osascript
Authored by: GaelicWizard on Apr 24, '10 10:58:35AM

But, of course, this method fails completely in any situation where Finder won't cooperate, such as:
(1) when Finder isn't running.
(2) when AppleScript (via osascript) won't work, e.g. when ssh'd in.
(3) when Finder is blocked waiting for something else, e.g. with a modal dialogue box on screen.

JP

Edited on Apr 24, '10 10:59:23AM by GaelicWizard



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: snuggles on Apr 22, '10 09:40:35AM

Nice hint. But a few caveats...

While redefining 'rm' might seem like a nice way to protect yourself, I think it's ultimately dangerous -- you get used to 'rm' being undoable and then, when you're on another system that hasn't been modified, you forget and end up hosed.

Also, there's no need to define a 'seq' function. Mac OS X has this already; it's just called 'gseq'.



[ Reply to This | # ]
gseq
Authored by: GaelicWizard on Apr 22, '10 04:48:34PM

Not on my system. The fact that it starts with "g" sounds like its from GNU. Mac OS X doesn't ship with much (any?) GNU software. What does "type gseq" in your Terminal say?



[ Reply to This | # ]
jot (not gseq)
Authored by: snuggles on Apr 23, '10 06:53:36AM
Bargh... my bad. It's in MacPorts. The Mac OS X (BSD) equivalent of seq is jot. The syntax is slightly different, but it does the same job.

[ Reply to This | # ]
re: jot (not gseq)
Authored by: GaelicWizard on Apr 24, '10 11:01:57AM

After checking jot out, I figured out how to make it work like seq. "jot - 2 6 3" works just like "seq 2 6 3".

Thanks!

I'll be updating this script on sourceforge (see hint for link) shortly.

JP

Edited on Apr 24, '10 11:10:44AM by GaelicWizard



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: alexk82 on Apr 27, '10 03:48:56PM

since i use a mac at home and at work i use on linux (ssh into prod boxes etc) i'll have i agree, i like to think of rm as dangerous and i need that fear so i'll be very careful when i use it.



[ Reply to This | # ]
Code fixes
Authored by: Kalak on Apr 22, '10 12:55:37PM
Since I have gnu tools from fink installed first in my path, I need to change the script to work with absolute paths for programs, and I've added a patch(Now I just need to convert this to use gnu tools to run on my linux boxes.)

Another tip to add is to really remove files immediately, use a backslash before "rm", so "\rm"

--- .bash.trash.sh 2010-04-22 15:51:40.000000000 -0400
+++ bash.trash.sh 2010-04-22 15:47:06.000000000 -0400
@@ -13 +13 @@
- command /bin/rm -i "$@"
+ command rm -i "$@"
@@ -20 +20 @@
- local HOME_DEVICE="$(/usr/bin/stat -f %Sd "$HOME")"
+ local HOME_DEVICE="$(stat -f %Sd "$HOME")"
@@ -31 +31 @@
- local DEVICE="$(/usr/bin/stat -f %Sd "$F")"
+ local DEVICE="$(stat -f %Sd "$F")"
@@ -46 +46 @@
- command /bin/rm -f "${TRASHCAN}"
+ command rm -f "${TRASHCAN}"
@@ -49 +49 @@
- echo "$TRASHCAN is inaccessible at this time." | /usr/bin/sed 's;'"$HOME"';~;g' 1>&2
+ echo "$TRASHCAN is inaccessible at this time." | sed 's;'"$HOME"';~;g' 1>&2
@@ -61 +61 @@
- if ! /bin/mv -vn "$F" "${TRASHCAN}/${FinT}"
+ if ! mv -vn "$F" "${TRASHCAN}/${FinT}"
@@ -68,2 +68,2 @@
- local TRASHSIZE="$(/usr/bin/du -hs "${TRASHCAN}" 2>/dev/null | cut -f 1)"
- local TRASHCANloc="$(dirname "$TRASHCAN" | /usr/bin/sed 's;^/Volumes/\(.*\)/.Trashes;\1;g' | /usr/bin/sed 's;'"$HOME"';~;g' | /usr/bin/sed 's;^/.Trashes;/;g')"
+ local TRASHSIZE="$(du -hs "${TRASHCAN}" 2>/dev/null | cut -f 1)"
+ local TRASHCANloc="$(dirname "$TRASHCAN" | sed 's;^/Volumes/\(.*\)/.Trashes;\1;g' | sed 's;'"$HOME"';~;g' | sed 's;^/.Trashes;/;g')"
@@ -78 +78 @@
- local MOUNTS=( $(mount | /usr/bin/sed -n 's:/dev/.* on \(.*\) (.*):\1:p') )
+ local MOUNTS=( $(mount | sed -n 's:/dev/.* on \(.*\) (.*):\1:p') )
@@ -84 +84 @@
- TRASH_SIZE="$( (for i in "${TRASHCANs[@]}"; do /bin/ls "$i"/; done) 2>/dev/null | wc -w)"
+ TRASH_SIZE="$( (for i in "${TRASHCANs[@]}"; do ls "$i"/; done) 2>/dev/null | wc -w)"
---
--
Kalak
I am, and always will be, an Idiot.
---
--
Kalak
I am, and always will be, an Idiot.
Edited on Apr 22, '10 12:56:45PM by Kalak


[ Reply to This | # ]
Code fixes
Authored by: GaelicWizard on Apr 22, '10 04:52:20PM

Just out of curiosity, why do you have broken tools in your path?

Serious question, I'm not just trying to flame/troll/whatever.



[ Reply to This | # ]
Code fixes
Authored by: Kalak on Apr 27, '10 07:44:05AM

They're not broken, they just work differently. The GNU tools installed by fink are what I'm more used to using from my linux experience starting when OSX was Nextstep, and I only wished I could afford the RAM to run it (early '90s). Some would ask the question of why OSX tools are "broken" because they use the BSD syntax. I see it as "you say tomato I say tomato" issue (ok, that doesn't type well). I've worked on both, but use the GNU tool much more than BSD tools. That's why I knew what the issue of why the script didn't work as posted on my box.

The changes will make sure the script works on any box, even with fink or macports installed, regardless of the user's PATH. The absolute path just makes sure your script uses the Apple installed utils, so you can focus on just supporting them, and will avoid a FAQ on why the script doesn't work.

---
--
Kalak
I am, and always will be, an Idiot.



[ Reply to This | # ]
Code fixes
Authored by: rahulbenegal on Apr 22, '10 11:17:41PM

Could you upload the modified code to gist and link it. I applied the patch but it gave failures.

Also, if someone converts this to a shell-script, please post a link. Thanks.



[ Reply to This | # ]
Code fixes (fixed for real this time)
Authored by: Kalak on Apr 29, '10 12:56:25PM
I reversed the order of the files when I created the diff. To use, save the hint's code to a file. I used .bash.trash.sh so it won't show in the finder, etc. Then run the following (which points to an updated diff):
curl http://iddl.vt.edu/~jackie/bash.trash.diff | patch -p0 .bash.trash.sh
you then source .bash.trash.sh with the line:
. ~/.bash_trash
try it, if you're happy, then remove .bash.trash.sh.orig---
--
Kalak
I am, and always will be, an Idiot.
---
--
Kalak
I am, and always will be, an Idiot.
Edited on Apr 29, '10 12:57:31PM by Kalak


[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: mb2696 on Apr 23, '10 08:21:49AM

This is great...although I noticed (not surprisingly) the "Put Back" feature isn't available for files put into the Trash with this method. Still an awesome tool, just pointing that out.



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: mb2696 on Apr 23, '10 08:31:09AM

Also, if a file is deleted and another file with the same name already exists in the trash, it will be overwritten. Finder tacks a number on the end...



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: SeanAhern on Apr 23, '10 06:31:11PM

Duplicates shouldn't be overwritten. The core 'mv' command from the script has a -n flag, which tells 'mv' to "not overwrite an existing file". I believe the script will just fail.

I agree that having some kind of sequence number would be useful.



[ Reply to This | # ]
Won't fail when file exists in trash
Authored by: GaelicWizard on Apr 24, '10 11:06:14AM

This script will not "just fail" if there is already an identically-named file in the trash. Instead, it will rename the more-recent file (the one being trashed presently) by prepending the date.

The hint explicitly states this. Re-read it if you missed it.

Edited on Apr 24, '10 11:08:41AM by GaelicWizard



[ Reply to This | # ]
Will not overwrite files already in trash
Authored by: GaelicWizard on Apr 24, '10 11:05:01AM

That's not true. This script will deliberately not overwrite an already-existing file. Instead, it will rename the more-recent file by prepending the date.

The hint explicitly states this. Re-read it if you missed it.

Edited on Apr 24, '10 11:07:09AM by GaelicWizard



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: Anonymous on Apr 23, '10 08:26:17PM

osx-trash (Rubyforce project page) is a much nicer way of trashing files from the command line.. "It uses AppleScript via Scripting Bridge on top of to communicate with the Finder", so deletes files exactly like Finder.

To empty the trash, you can use a single line of AppleScript:

osascript -e 'tell app "Finder" to empty the trash'

..and as a bash alias:

alias emptytrash="osascript -e 'tell app \"Finder\" to empty the trash'"

[ Reply to This | # ]
Is there a trash spec ?
Authored by: rahulbenegal on Apr 23, '10 10:09:06PM

Is there a spec for how to interact with trash as in freedesktop.org:
http://www.freedesktop.org/wiki/Specifications/trash-spec

I found a script named tr.sh (perl) which works with Linux and other systems. The author wants to now if there's a spec so he can modify it for OS X. It maintains a folder called files and info, and a metadata file. It stores date of deletion, folder info (so no overwriting similar named files).



[ Reply to This | # ]
No trash spec.
Authored by: GaelicWizard on Apr 24, '10 11:19:16AM

The spec on freedesktop is not a "this is how things work" spec, but a "this is how we want things to work" spec. That script you found may be the only script which uses the spec, or maybe KDE and GNOME use it too. Mac OS X's trash is an entirely different beast using the same user-level metaphor. Microsoft Windows' Recycle Bin is, likewise, an entirely different beast from either of these, again using the same user-level metaphor. That tr.sh script will happily work just fine on Mac OS X, but it won't interact with Finder's trash unless you tell it to re-use the hidden folders which Finder uses, in which case it will likely only half-work. Likewise, Finder would likely empty tr.sh's metadata along with the rest of the trash.

The Finder itself actually treats the trash differently depending on which version of Mac OS X is running. The primary difference is in the formatting of the date and/or sequence number for adding duplicate files to the trash.

Additionally, there is no metadata of any sort stored in the trash until 10.6, where only "original location" is stored. (I have no idea _where_ that's stored. Once I figure it out, I'll update this script on sourceforge (see hint for link).)



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: lihtox on Apr 24, '10 11:33:13AM

Personally, I just use "mv -i file_to_delete ~/.Trash".



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: GaelicWizard on Apr 24, '10 11:37:54AM

That's basically what this script does, but with two enhancements:
(1) less typing.
(2) it won't copy a 30GB file from /Volumes/MyBigFiles/file_to_trash into your home folder.

The hint explains what its for at the beginning, fyi.



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: lihtox on Apr 25, '10 10:02:48AM

I'm sorry; I did get the point of your code and didn't mean to be critical, but in hindsight I see that it could be taken that way (particularly on the Internet). Mostly I just wanted to see if anyone would respond with "Oh, you're not supposed to do that because it breaks X and Y." I don't normally work with multiple volumes (other than disk images), but if I start I will keep your solution in mind!



[ Reply to This | # ]
Check for Update on Sourceforge.
Authored by: GaelicWizard on Apr 24, '10 11:34:31AM

I've posted an update of this hint to the [MacTrash](http://mactrash.sf.net/) page on Sourceforge. Check it out for the latest version.



[ Reply to This | # ]
Check for Update on Sourceforge.
Authored by: Anonymous on May 04, '10 01:17:29PM

Um... I don't see any files. The list is totally empty.



[ Reply to This | # ]
Use OS X's trash in a Finder-like way from Terminal
Authored by: ehunt123 on Apr 30, '10 08:53:21AM

Surprised this gem has not been mentioned. It's an ObjC clone of the osx-trash util in ruby by a developer that also utilizes the scriptingbridge (10.6 only):

http://hasseg.org/trash/



[ Reply to This | # ]