I hacked together a command-line implementation of the Freedesktop.org Trash Can Specification in Bash 3.2 one evening:
#!/bin/bash # trash (version 0.2.1) # Robert Rothenberg # This is a bash script implementation of the FreeDesktop.org Trash # Specification. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. if [ -z "$1" ]; then echo "Usage: trash FILE..." exit 1 fi # sed script to encode filenames sedscript='s/ /%20/g s/!/%21/g s/"/%22/g s/\#/%23/g s/\$/%24/g s/\&/%26/g s/'\''/%27/g s/(/%28/g s/)/%29/g s/\*/%2a/g s/+/%2b/g s/,/%2c/g s/-/%2d/g s/:/%3a/g s/;/%3b/g s//%3e/g s/?/%3f/g s/@/%40/g s/\[/%5b/g s/\\/%5c/g s/\]/%5d/g s/\^/%5e/g s/_/%5f/g s/`/%60/g s/{/%7b/g s/|/%7c/g s/}/%7d/g s/~/%7e/g s/ /%09/g' function url_encode { echo $1 |sed -e "$sedscript" } function get_trashdir { mounts=`cat /etc/fstab |grep -v \# |awk '{print $2}'` base=/ if [ "$EUID" != "0" ]; then mounts="$HOME $mounts" fi for i in $mounts do if [[ $1 =~ ^$i ]] then if [[ $i =~ ^$base ]] then base=$i fi fi done if [ "$base" != "$HOME" ]; then trashdir="$base/.Trash/$UID" if [ ! -d "$trashdir" ]; then trashdir="$base/.Trash-$UID" fi mkdir -p "$trashdir" if [ "$?" != "0" ]; then base=$HOME fi fi if [ "$base" == "$HOME" ]; then base=$XDG_DATA_HOME if [ -z "$base" ]; then base="$HOME/.local/share/" fi trashdir="$base/Trash" fi echo $trashdir } for f in "$@" do # get full pathname of file filename=$(readlink -f "$f") dir=${filename%/*} trashdir=`get_trashdir "$dir"` mkdir -p "$trashdir/files" if [ "$?" != "0" ]; then echo "Unable to write to $trashdir" 1>&2 exit 2 fi mkdir -p "$trashdir/info" if [ "$?" != "0" ]; then echo "Unable to write to $trashdir" 1>&2 exit 2 fi trashname="${filename##*/}" origname="${trashname%%.*}" ext=".${trashname##*.}" if [ "$ext" == ".$trashname" ]; then ext="" fi cnt=1 while [ -e "$trashdir/files/$trashname" ] || \ [ -e "$trashdir/info/$trashname.trashinfo" ]; do trashname="${origname}_${cnt}${ext}" let cnt=cnt+1 done deletedfile="$trashdir/files/$trashname" deletedinfo="$trashdir/info/$trashname.trashinfo" canon=`url_encode $filename` cat > "$deletedinfo" <<END [Trash Info] Path=$canon DeletionDate=`date +"%FT%H:%M:%S"` END mv -v "$filename" "$deletedfile" done exit 0
It's quick-and-dirty hack, so I won't vouch for how well it follows the spec, or even works. But it was interesting to see how closely it could be implemented in Bash using as few external commands as possible: readlink, mv, mkdir, cat, awk, sed (the URL-encoding routine comes from here).
If you're looking for a proper implementation, see trash-cli here.
UPDATE (2 March 2011) An updated version of this script is now on GitHub here.