Apr 22, '10 07:30:00AM • Contributed by: GaelicWizard
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
}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.]
