You are not logged in.
How many times have you sworn under your breathe because you typed
rm last-night-was-awesome-man.jpginstead of
rm last-night-was-NOT-awesome-man.jpgWell, odds are, at least once or twice.
It's frustrated me on several occasions when I lost files I'd rather not have, so I set out to build a trash bin for Linux.
Really, it was a lot simpler than I thought it would be.
In summary, all I needed was a new directory and two one-line bash scripts.
So, let's dive in.
I'll start off by saying that if you plan to use this trash bin for all files you plan to rid of, you can just forget that the rm command even exists.
By the time you finish here, it'll be replaced by del and empty-trash.
First, create a directory for your trash.
Start your terminal and type the following:
mkdir ~/.trashFor those unfamiliar with bash, mkdir makes directories and ~/.trash is the directory made in your home directory.
Next up, we need to make a script to move files to the new trash directory.
Start up your text editor of choice (nano and you know it) and type the following:
#!/bin/bash
mv $* ~/.trashSave the file as del
For those who are unfamiliar with bash, #!/bin/bash simply tells your terminal to use bash, mv is used to move files from one location to another, $* represents the argument to be passed (in this case a file, or multiple files (This will be demonstrated later)), and ~/.trash is the directory the files will be moved to.
Now, we need to make the script executable.
chmod +x delNow, in order to use this script from anywhere in the terminal, we need to move it to /usr/local/bin
sudo mv del /usr/local/binNow then, let's test this out.
Find that picture of the night that never happened and type the following:
del me-onstage.jpgNow type this:
ls ~/.trashYou should see the picture there and if you do, del works!
But wait, now you have to go to the .trash directory every time you want to get rid of the files, what a hassle!
Solution?
Another simple script.
So fire nano back up and enter the following:
#!/bin/bash
rm -r ~/.trash/*Save the file as empty-trash
For those who are unfamiliar with bash, rm -r allows you to delete both files and directories and ~/.trash/* is how rm -r knows what files to delete.
Now move it to /usr/local/bin, like you did with del.
sudo mv empty-trash /usr/local/binNow type empty-trash into your terminal, then enter:
ls ~/.trashThere should be no output.
If there isn't, then the evidence of your darkest-hour is now gone forever.
Congratulation!
You now have a functional trash bin!
Hold on a second!
It turns out that there are two more pictures, a text file and a movie exposing your exposing of yourself... err, I didn't tell everyone that...
Anyway, now you delete them.
It's as simple as this:
del i-lost-my-pants.jpg there-they-are.jpg i-know-what-you-did.txt the-new-richard-simmons.aviAll of the evidence is in your trash bin.
Now we make sure your girlfriend never finds out!
empty-trashCongratulations.
Last night never happened and no one will ever know.
Except for the 8500 people who watched you do it...
A quick note, I don't know of any way to restore files from the trash bin other than doing it manually like so:
mv deleted-file ~/original/directoryIf anyone knows of a method of restoring files to their previous directory other than that, please let me know.
Last edited by Chives (2010-02-27 05:02:14)
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
I wonder if
mv "$@" ~/.trashmight not work better with files with spaces in the names?
About restoring files, you need some way of recording where it came from, so you can send it back.
...just a moment, how about joining in the system used by Thunar (and maybe others) ie:
deleted files go to ~/.local/share/Trash/files/
info about deleted files goes in ~/.local/share/Trash/info/
If you have a look in the info folder, you'll find files like mydubiousfile.jpg.trashinfo
which are text files with something like:
[Trash Info]
Path=/home/john/pictures/mydubiousfile.jpg
DeletionDate=2010-02-20T14:00:29You could modify your script to something like this?
#!/bin/bash
for i in "$@"
do
mv $i ~/.local/share/Trash/files
echo "[Trash Info]
Path=$(readlink -f $i)
DeletionDate= $(date +%FT%T)" > ~/.local/share/Trash/info/${i}.trashinfo
done
exitI've run out of time so I can't actually check this but it might work...
Play around with it. 
Also, there are other complications- what if you try to "del" a symlink? Will it delete the linked file instead? Watch out!
John
--------------------
( a boring Japan blog , and idle twitterings )
Offline
Well, how is the trash bin handled in Thunar and Gnautilus? We could try reverse-engineering that...or looking at the source code.
while ( ! ( succeed = try() ) );
We've earned a reputation as a nice, friendly community; please help us keep it that way.
Online
....
I think you're starting to over analyze this...
I just use PCManFM and usually do most of my file management from the terminal anyway...
I just made this in case I delete the wrong file...
However, johnraff, I'll look into your info more later, but right now I'm so tired that I'd end up deleting my file system if I tried...
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
here you go, could be a little more functional, but the basics are all there:
del
#!/usr/bin/env python
import os,shutil
from optparse import OptionParser
home = os.environ.get('HOME')
trash = home + "/.trash"
infoPath = trash + "/info/"
filesPath = trash + "/files/"
if not os.path.exists(infoPath):
os.makedirs(infoPath)
if not os.path.exists(filesPath):
os.makedirs(filesPath)
parser = OptionParser()
(options, args) = parser.parse_args()
for arg in args:
if not os.path.exists(arg):
print "invalid file " + arg
else:
info = open(infoPath + arg, "w")
info.writelines(os.path.realpath(arg))
info.close()
if os.path.exists(filesPath + arg):
if os.path.isdir(filesPath + arg):
shutil.rmtree(filesPath + arg)
else:
os.remove(filesPath + arg)
shutil.move(arg, filesPath)empty-trash
#!/usr/bin/env python
import shutil,os
home = os.environ.get('HOME')
trash = home + "/.trash"
shutil.rmtree(trash)
print "trash emptied"restore
#!/usr/bin/env python
import shutil,os,sys
from optparse import OptionParser
home = os.environ.get('HOME')
trash = home + "/.trash"
infoPath = trash + "/info/"
filesPath = trash + "/files/"
parser = OptionParser()
parser.add_option("-l", "--list", dest="list", action="store_true",
help="list contents of trash")
(options, args) = parser.parse_args()
if options.list:
files = os.listdir(infoPath)
for file in files:
info = open(infoPath + "/" + file)
print info.readline()
info.close()
sys.exit()
for arg in args:
if not os.path.exists(filesPath + arg):
print "file " + arg + "does not exist"
else:
info = open(infoPath + arg)
path = info.readline()
info.close()
shutil.move(filesPath + arg, path)
os.remove(infoPath + arg)
print "restored " + pathrestore -l will list the files in the trash by their full path, to restore though use:
restore filename (not the full path)
that's the only quirk with it right now that I can think of.
oh also you can only have one file with the same name in the trash at a time, even if the full path is different... I may try to figure out a way to fix it later, not sure.
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
PCMan complained bitterly somewhere in the PCManFM docs about how hard it was to make a standards-compliant trash can (presumably why there isn't one in the current PCManFM). It isn't as simple a problem as it seems apparently... symlinks, ownership, permissions...
iggykoopa: that's python - it looks really neat - I feel as if I can almost read it, without knowing it at all! I guess learning a bit of python should be my next project... 
John
--------------------
( a boring Japan blog , and idle twitterings )
Offline
Ownership and permissions should be preserved by the scripts I posted(thought about it some more and I think I'm wrong on that), symlinks I think are the only problem. I haven't tried it yet, but because I use real path I believe it will actually follow the symlink and delete the file it points to, not just the link.
Python is an awesome language, the readability is a big reason why it has become so popular. The scripts I wrote took me about a half hour including testing to write, which I think is pretty good for something like that. That was with having to look up a bunch of the functions I used too, since I'm a little rusty now.
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
this is one i wrote a while ago, (when i was on ubuntu) so the paths may need modifying (yes the trashcan specification doesn't define a path for the trash) but everything else should be ok.
its got options for listing and restoring the trash can contents aswell
the only functionally different thing to most trash implementations, is that all trash gets sent the ~/.local/share/Trash/files rather than /current/drive/.local/share/Trash/files
#!/usr/bin/env python
#
# bin.py
#
# Copyright 2009 Ben Holroyd <holroyd.ben@gmail.com>
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
# pretty much compatible with gnome trashbin although everything is sent to main
# trashbin in ~/.local/share/Trash/files rather than devices local trash file in
# i prefer this behaviour but it is not compatible with that stipulated in the
# freedesktop.org trashcan specification.
import os, sys
class Path():
""" deals with all things todo with paths... obviously"""
import os
def __init__(self ):
self.home = os.environ['HOME']
self.delete_path = self.home +'/.local/share/Trash/files' # join doesn't work
self.info_path = self.home +'/.local/share/Trash/info'
self.info_suffix = '.trashinfo'
def filename (self,arg):
return os.path.split(arg)[1]
def path_sans_filename (self, arg):
return os.path.split(arg)[0]
def full_delete_path(self,arg):
return os.path.join(self.delete_path,self.filename(arg))
def full_info_path(self,arg):
return os.path.join(self.info_path,self.filename(arg))+ self.info_suffix
def path (self,arg):
""" expands and cleans up path in every conceivable way."""
return os.path.realpath(os.path.normpath(os.path.abspath(os.path.expanduser(arg))))
def join (self,arg,arg2):
return os.path.join(arg,arg2)
def ls (self, dir):
return os.listdir(dir)
def get_date():
import datetime
return datetime.datetime.now().strftime('%Y-%m-%dT%T')
def test_duplicate(filename, path):
""" tests whether the file name already exists in the deleted file folder
follows behaviour will add starting from 2 'prefix.x.suffix'."""
index = 2
filenametmp = filename
while os.access(Path().join(path,filenametmp), os.F_OK) == True:
if filename.find('.') != -1:
filenametmp = filename[:filename.find('.')] + '.' + str(index) + filename[filename.find('.'):]
else:
filenametmp = filename + '.' + str(index)
index += 1
return filenametmp
def trash_info(arg,filename, path):
#split filename and path within this function?
"""Writes files to the trash info directory"""
fullpath = path.path(arg) #get full path for file to delete
file = open(path.full_info_path(filename),'w')#get full path for new trashinfo file
file.write('[Trash Info]\n')
file.write('Path=%s\n' % fullpath)
file.write('DeletionDate=%s\n' % get_date())
file.close()
def main():
from optparse import OptionParser
path = Path()
parser = OptionParser(
version=" %prog V0.1-1 <holroyd.ben@gmail.com>",
description = "%prog - trash bin functionality for the commandline.",
usage = "%prog [-dtrli] [--prompt] [--delete] [--trash] [--restore]\n [--list] [--verbose] [--help] [--version]")
parser.add_option("-d","--delete",action="store_true",dest="bldelete",default=False,help="permanently delete entries in the trash bin." )
parser.add_option("-t","--trash", action="store_true" , dest="bltrash", default=False, help="send a file to the trash bin")
parser.add_option("-r","--restore",action="store_true", dest="blrestore",default=False, help="restore a file from the trash bin")
parser.add_option("-l","--list",action="store_true", dest="bllist",default=False, help="list files currently in the trash bin")
parser.add_option("-v","--verbose" ,action="store_true",dest="blverbose",default=False,help="be verbose")
parser.add_option("-i","--prompt" ,action="store_true",dest="blprompt",default=False,help="print confirmation prompt when deleting, trashing or restoring files")
global options
(options, args) = parser.parse_args() #actually parse args
if options.bldelete == False and options.bltrash == False and options.blrestore == False and options.bllist == False:
if len(args) == 0:
options.bllst = True
else:
options.bltrash = True
if options.bldelete ^ options.bltrash ^ options.blrestore ^ options.bllist: pass
else: parser.error("options can't be combined.");
if options.bltrash == True:
from shutil import move
for arg in args:
if arg == '.' or arg == '..':
sys.stderr.write(" error: '.' and '..' aren't valid filenames")
pass
if len(arg) > 0 and os.path.exists(path.path(arg)):
#expands '~', sorts '..' etc and gives absolute, real path
if options.blprompt == True:
if raw_input("send '%s' to trash ?" % arg) in ['y','Y','yes','YES']: pass
else: continue
fullpath = path.path(arg)
filename = path.filename(arg)
filename = test_duplicate(filename, path.delete_path)
move(fullpath, path.full_delete_path(filename))
trash_info(arg, filename, path)
if options.blverbose == True:
print "%s successfully sent to trash" % path.join(os.getcwd(),filename)
if options.bllist == True:
for file in path.ls(path.delete_path):
mode,inode,dev,nlink,uid,gid,size,atime,mtime,ctime = os.stat(path.full_delete_path(file))
#get modified date
from datetime import datetime
date = datetime.fromtimestamp(mtime).strftime('%d-%m-%Y %H:%M')
size = float(size)
#format size
size_index = 0
while size > 1024 and size_index <= 4:
size = size/1025
size_index +=1
size_names = ['B','K','M','G','T']
size_index = size_names[size_index]
#get name of owner
import pwd
username = pwd.getpwuid(uid).pw_name
#get file type
if os.path.isdir(path.full_delete_path(file)):
filetype = 'd'
blfiletype = True
elif os.path.isfile(path.full_delete_path(file)):
filetype = 'f'
blfiletype = False
else:
filetype = '?'
blfiletype = False
#get deletion date
fp = open(path.full_info_path(file),'r')
dd = ''
for lines in fp:
if lines.startswith('DeletionDate'):
dd = lines [13:-1]
dd = dd.replace('T',' ')
print " %s %s %.2f%s\t%s" % (filetype,username,size,size_index,dd),
if blfiletype == False: print file
else: print "\033[34;02m%s\033[00;00m" % file #print file in colour
if len(path.ls(path.delete_path)) == 0:
sys.stderr.write('Trash is empty\n')
if options.bldelete == True:
def delete_entries(arg, path):
import shutil
try:
if options.blprompt == True:
if raw_input("permanently delete '%s'?" % arg) in ['y','Y','yes','YES']: pass
else: return
if os.path.isdir(path.full_delete_path(arg)):
shutil.rmtree(path.full_delete_path(arg))
else:
os.remove(path.full_delete_path(arg))
os.remove(path.full_info_path(arg))
if options.blverbose == True:
print '%s deleted' % arg
except:
sys.stderr.write('problem deleting %s\n' % arg)
if len(args) == 0: #delete all if no argument
for arg in path.ls(path.delete_path):
delete_entries(arg, path)
else:
for arg in args:
delete_entries(arg, path)
if options.blrestore == True:
from shutil import move
for arg in args:
try:
path.ls(path.delete_path).index(arg) #find can't be used on lists
originalpath = ''
file = open(path.full_info_path(arg))
for lines in file:
if lines.startswith('Path'):
originalpath = lines[5:-1] # get original filename
# and check name doesn't already exist in file
originalpath = path.join(path.path_sans_filename(originalpath),test_duplicate(path.filename(arg), originalpath))
file.close()
if options.blprompt == True:
if raw_input("restore '%s'?" % arg) in ['y','Y','yes','YES']: pass
else: continue
move(path.full_delete_path(arg),originalpath)
os.remove(path.full_info_path(arg))
if options.blverbose == True:
print '%s restored to %s' % (arg, originalpath)
except ValueError:
sys.stderr.write('%s not found\n' % arg)
return 0
if __name__ == '__main__': main()- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
I've been thinking about this some more and and idea came to me.
I'm fairly acquainted with C and one field I'm looking more into is the its file capabilities.
I figured that this would be a good way to learn them better.
When I write this program, I'm going to adjust how it works so that all functions of the trash bin are in one program called trash.
Here's a synopsis of how it would work.
It's just one simple command, trash, which will take one (or more) of several arguments.
For example, if you wanted to delete a file:
trash linkin-park.jpgThat's all it would take.
If you wanted to empty the trash bin:
trash -eI'm considering making this a little more verbose by changing e to empty, or making both arguments available.
And, using the information you guys made available, restoring:
trash -r my-house.aviAgain, with r, I'm considering changing it to restore or making both avaible arguments.
So, what do you guys think?
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
sounds pretty good, similar to my implementation, but with just one executable. I would look into what it would take to preserve permissions and the like while your at it to. I usually just go with python for stuff like this because it isn't much overhead and you don't need to compile. It should be a good project to learn with though 
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
sounds pretty good, similar to my implementation, but with just one executable. I would look into what it would take to preserve permissions and the like while your at it to. I usually just go with python for stuff like this because it isn't much overhead and you don't need to compile. It should be a good project to learn with though
what permissions would there be issues with preserving ? moving files shouldn't affect them, or are you refering to ownership permissions.
+1 for python, although it would be a good learning exercise for any language.
- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
Python is good, but I have some sort of attraction to natively compiled languages, like C or C++
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
what permissions would there be issues with preserving ? moving files shouldn't affect them, or are you refering to ownership permissions.
+1 for python, although it would be a good learning exercise for any language.
I was thinking if you used it as root, then tried to restore it, I think it would copy back as root. Haven't tested it though, so it might work as is now.
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
You shouldn't be allowed to move files that aren't in your directory without root permissions.
I can't imagine my program would be any different.
sudo trash your-houseYou should have to sudo to work with files outside your user directory.
So isn't this a natural file permission handle that's already built into the kernel?
maybe I'm missing something, but wouldn't that suffice?
Last edited by Chives (2010-03-01 06:01:35)
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
What I was saying though is wouldn't it copy the files back with ownership as root? Instead of the original owner.
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
What I was saying though is wouldn't it copy the files back with ownership as root? Instead of the original owner.
i would't have thought so, sudo mv/cp don't change permissions, you need chmod and chown to do that, the only issues i can see with sudo is:
A. where to put the trash /root/trash or ~/trash
B. you would need sudo permissions to empty the trash
EDIT:
sorry i understand now, sudo cp'ing something with ownership ben changes to ownership to root, although sudo mv doesn't.
going out on a limb, I would guess that both the python shutil devs and the mv and cp devs have spent more time on reading posix standards than us, and so are probably implementing the correct behaviour already.
a quick test shows that (my implementation atleast) behaves the same as mv, maintaining ownership permissions.
Last edited by benj1 (2010-03-01 14:20:57)
- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
oh ok, I was testing with cp not mv, so as long as your mv'ing the files you should be good 
I say never be complete, I say stop being perfect, I say lets evolve, let the chips fall where they may.
Offline
oh ok, I was testing with cp not mv, so as long as your mv'ing the files you should be good
i was testing with mv, thats why i didn't realise what you were on about to start with, i have to say i don't quite understand the behaviour of cp, i would have thought it would be consistent with mv, but it seems to be more consistent with touch et al, i suppose if you treat it as a file creation tool it would make sense but i would classify it as a file modification/management tool along with mv etc.
anyway ill stop now, this is starting to read like a geeky file system developers thread 
- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
A. where to put the trash /root/trash or ~/trash
B. you would need sudo permissions to empty the trash
fwiw, XFCE's implementation is to have multiple trashcans:
~/.local/share/Trash for each user, and
/root/.local/share/Trash for root.
Stuff deleted while running Thunar as root goes in root's trashcan, so of course needs to be restored by root too.
'sudo del file' could do the same.
John
--------------------
( a boring Japan blog , and idle twitterings )
Offline
benj1 wrote:A. where to put the trash /root/trash or ~/trash
B. you would need sudo permissions to empty the trashfwiw, XFCE's implementation is to have multiple trashcans:
~/.local/share/Trash for each user, and
/root/.local/share/Trash for root.
Stuff deleted while running Thunar as root goes in root's trashcan, so of course needs to be restored by root too.
'sudo del file' could do the same.
I think thats the standard implementation for most trash implementations, i was just thinking more from an intelligent defaults point of view, if i log in as root and delete something, do i want it going to the same place as if i sudo delete something, conversely as a normal user do i want deleted items and sudo deleted items going to different places?
its not a huge problem i know, i find the behaviour for removable media much more annoying.
- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
if i log in as root and delete something, do i want it going to the same place as if i sudo delete something, conversely as a normal user do i want deleted items and sudo deleted items going to different places?
I think that's the safest option. Unfortunately, even working with sudo, $HOME still points to the user's home, not root's:
john@raffles3:~$ sudo whoami
root
john@raffles3:~$ sudo echo $HOME
/home/johnso a del script would have to use whoami or something to determine which trash folder to use.
i find the behaviour for removable media much more annoying.
Which? Putting the trash on the hard disk, as thunar now does, or on the medium, as it used to do?
Last edited by johnraff (2010-03-03 05:34:46)
John
--------------------
( a boring Japan blog , and idle twitterings )
Offline
Which? Putting the trash on the hard disk, as thunar now does, or on the medium, as it used to do?
putting it on the removable media, i didn't realise it had changed, good to know 
- - - - - - - - Wiki Pages - - - - - - -
#! install guide *autostart programs, modify the menu & keybindings
configuring Conky *installing scripts
Offline
Just an update on this trash-bin I'm working on.
I've been pretty busy this week so I haven't been able to get much work done it the program.
Hopefully I'll have a little time next week to work on it.
I really am a nice person until you ask the police.
The first link is to a forum for a kick-ass MMORPG to be
The second is a blog for anyone with an opinion.
|The Hallow Life|DigiMantis|
Offline
Copyright © 2012 CrunchBang Linux.
Proudly powered by Debian. Hosted by Linode.
Debian is a registered trademark of Software in the Public Interest, Inc.