27
August - 2014
Wednesday
SUBSCRIBE TO NEWS
SUBSCRIBE TO COMMENTS

Ever Forward

Always discovering, learning, moving… ever forward

Progress With Bash Trashman

Posted by donp On March - 20 - 20122 COMMENTS

As I walk through learning shell scripting, my trashman project is starting (slowly) to take shape.  I have made a number of changes and am posting the updated version here, as I do not yet have revision control setup.  Still, I think it is greatly improved, even if still not complete.  Also, one person suggested calling it “Brash”, as in “Bash tRash“.  That is an interesting idea, but I would like some input.  I have also discovered that there is another project out there that is complete, and looks fairly well thought out.  However, as I am doing this to learn, I shall thus forge ahead.

As I posted previously, my Trashman script was “crudimentary” and incomplete.  Thus some re-writing of the code was in order, namely to build on the foundation.  But I also changed a few fundamental things.  For one, I changed the script to tm, so one can call it simply:

tm [action] file1 dir/file2 dirname/

Other changes include:

  • retrieve is now called restore. Since most trash programs use the term “restore”, people will remember that better
  • history is now called info. There is already a bash program called history.  It occurred to me it might be better to not create confusion.
  • Trashman can now handle multiple files and even whole directories. It does not (yet) record all of the individual files of a directory to the trash log.
  • If you call it without any arguments at all (that is, just run it as tm, with no action or files), it will give a usage message, indicating that you need to include at least one argument.

I mentioned the name suggestion.  Brash has the possibility of being verbalized, as in “we can just brash these files”.  And one can run the script as brash [action] file1.  On the other hand, running tm is a lot like running ls or rm.  I don’t really have a preference, so am open to suggestions.

I mentioned I am doing this as a way to learn shell scripting.  I should share a lesson that might be useful for others.  While syntax and misspelling errors are probably the easiest errors for a newbie to make, they certainly are not the only ones.  Your syntax can be great, but your error may lie elsewhere, such as where your program happens to be looking for files.  In this case, I was testing for the existence of the files in both the toss() and restore() functions, and it worked great in toss(), but not in restore().  Only I did not realize my file test was failing – I thought it was something to do with the syntax, even though both functions were mostly the same.  I finally figured it out when I started commenting out sections of code and using echo statements to test things out.

I first tested the function call itself (in the main section).  That worked.  Then I called the function with just an echo statement, and that worked also.  Then I added the file variable to the echo statement, and that worked.  So the program was calling the function and the function was able to work with the variable.  I then created a simplified for loop to echo each file invidivually, and that worked.  Then it hit me…  the file test was failing.  But why?  Then I realized that it was likely failing because I was testing for the existence of the files in the PWD (Present Working Directory), and not in the trash directory.  To test my hypothesis, I created a small script to just test the existence of files.  Sure enough, if I tested for a file outside the PWD, without specifying it’s location, the script failed to find it.

Users are not going to want to specify the trash directory every time they want to restore a file – and they should not have to.  You will see in the code where I have commented out the file test, but the quick-n-easy solution (though probably not the best) is to cd into the trash directory before testing the files.  Then the test will work as expected.  Better still, I need to define a $TRASHDIR variable and make sure the program looks there for files that are already in the trash can.  Oh, I’ve got some more lessons to learn yet, but I am both learning and making progress with the script.  And that’s the point of the exercise.

Calling trashman with help, a file to toss, the list and history

Also, here are a few examples of  how to run Trashman:

Tossing files in the trash:
$ tm file1 file2 file3 dirname/file4
$ tm dir1/

Here is an example of the message telling the user the directory is in the trash can:
$ dir/ sent to trash Tue Mar 20 09:29:16 EDT 2012

You can restore files
$ tm restore file1 file2 dir1/
You can get a history of deletions and restorations:
$ tm info

Anyway, here is the updated version of Trashman:

#!/bin/bash
# trashman - a trash bin for Bash
# Copyright (C) 2012 Donald C. Parris
# License - GNU General Public License version 3:
#    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 3 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, see <http://www.gnu.org/licenses/>.
# You may find a copy of the license here.  http://www.gnu.org/licenses/gpl.html
# v. 0.0.3
# No warranties of any kind - use at your own risk!

### Functions ###
help()
{
echo "  Trashman is a trash manager for the Bash shell."
echo "  Files are stored in ~/.trash/can | ~/.trash/log/trashlog"
echo "  trashman creates these directories and log file on the first run"
echo " "
echo "  tm [action] file1 file2 dir/file3 dir2/ -- acts on the file(s)/dir(s)"
echo "Examples: "
echo "  tm [path/]file1 -- tosses 'file_name' in trash"
echo "  Since toss is the default action, you do not have to run:"
echo "  tm toss path/file_name, but you can"
echo "  tm dirname/ sends the directory AND all its files to the trash"
echo "  tm restore dirname/ restores the directory AND its files."
echo " "
echo "  tm list -- prints a list of files in the trash can"
echo "  tm restore file_name -- restores tossed file to ~/Documents"
echo "  tm info -- view the trashman log"
echo "  tm empty -- permanently deletes (rm) files in ~/.trash/can"
echo "Notes: "
echo "  toss requires path if file is not in working directory"
echo "  restore does not need a path for stored file. See below"
echo "  not yet implemented -- restore file to user-specified location"
echo "  Files are restored to your home directory. See restore() below"
}

# Ensure we have a (preferably hidden) place to store 'deleted' files
# and a place to store a log file.
MkTrashCan()
{
mkdir --parents ~/.trash/can
mkdir ~/.trash/log
exit 0
}

# Here we toss out the trash. Note the use of date to timestamp the
# transaction. It is also appended to the log. If log does not exist, it is
# created automatically. Then we actually move the file into the trash can.
toss()
{
for f in $filelist
do
    if [ -f "$f" -o -d "$f" ]
    then
        SOURCEFILE=$f
        echo "${SOURCEFILE#/*/} sent to trash `date`" | tee -a \
        ~/.trash/log/trashlog
        mv $f ~/.trash/can
    fi
done
}

# The user may want to see a list of files they tossed in the trash.
list()
{
ls -al ~/.trash/can
}

# If the user wants, they can retrieve a file.  Again, this is timestamped
# and written to the log.
restore()
{
for f in $filelist
do
#    Need to cd into trashdir to run this test (not complete)
#    if [ -f "$f" -o -d "$f" ]
#    then
        SOURCEFILE=$f
        # Edit the target "~/" to change default restore location
        mv ~/.trash/can/$f ~/   
        echo "${SOURCEFILE#/*/} has been restored as of `date`" | tee -a \
        ~/.trash/log/trashlog
#    fi
done
}

# Users can view the history to see what moved which way when.
info()
{
cat ~/.trash/log/trashlog
}

# If the user so chooses, all files can be permantly deleted.
empty()
{
rm -r ~/.trash/can/*
}

### Main Body ###
# Test whether .trash/can and log directories exist
if test ! -d "$HOME/.trash/can"
    then
        echo "Trash can does not yet exist. Let's create it now."
    MkTrashCan
fi

if [ $# -eq 0 ]; then    # Must have at least one argument.
    echo "usage: tm [action] file1 [file2]"
    elif [ -f "$1" -o -d "$1" ]   # if this is a file or directory
    then
        filelist="$@"
        toss
    # if it is NOT a file AND not a directory, it's an action.
    elif [ ! -f "$1" -a ! -d "$1" ]
    then
        action="$1"
        shift
        filelist="$@"
        if [ $action == "toss" ]
            then
                toss
            elif [ $action == "restore" ]
            then
                restore
            elif [ $action == "empty" ]
            then
                empty
            elif [ $action == "list" ]
            then
                list
            elif [ $action == "info" ]
            then
                info
            elif [ $action == "help" ]
            then
                help
            else
                if [ $action == " " ]; then
                  echo "$action is invalid"
                fi
        fi
fi

Edit 23-Apr-2012: Please note that I have been making progress with this script: Brash: Restore Files Where You Want Them

2 Responses to “Progress With Bash Trashman”

  1. [...] because I know my friend Don Parris has his own explanation for it (which he outlines on his blog here), but it appears that while building up his bash scripting skills, he noticed a certain finality to [...]

  2. [...] to actually do things.  As an example, I have referred back to the first book in writing my own trash can shell script.  Shell scripting is (a) covered by the Linux+ exam, and (b) something every system [...]