Creating a nondestructive 'rm'

Mark Ikemoto marki at hpiacla.HP.COM
Sun Nov 6 14:46:51 AEST 1988


Try this.  It runs a little too slow for me but maybe you're running on
a faster machine.  

( Matt, for the b_on, b_off, u_on, u_off script variables, you'll need to
replace <esc> with the real escape character so the bolding will work
on your HP terminal.  I deliberately used <esc> so other people's non-HP
terminals accessing Notes wouldn't get screwed up by the escape chars
when they looked at this script. )


Mark

------------ CUT LINE: Remove this line and everything above it ------------
 # Bourne shell script
#######################################################################
#
# @(#) TITLE:     Safer rm command
#
#
# DESCRIPTION:    See user man page below.
#
# INSTALLATION:
#
#    Save this to a file.  Change user-defined shell vars after the
#    script header to your taste and terminal.  Set the permissions on
#    this script to 'execute'.  Invoke this script with no parameters
#    to see the embedded man page.
#
# UPDATE HISTORY:
#
# what date/who      type of change
# ---- --------      --------------
#      01/18/88
#      Mark Ikemoto  >Script created.
#
#      03/03/88
#      Mark Ikemoto  >Added touch command to give file the current time
#                     so it isn't cleaned up until it is older than the
#                     system backup timestamp file.
#
# @(#) 11/05/88
#      Mark Ikemoto  >Added much more comments.
#                    >Added pseudo support for the -f option.
#                    >Isolated HP terminal escape chars into shell vars.
#
#######################################################################
# Shell user-defined functions 
#  


####################################################################
# Start of main commands
#


#-------------------------------------------------------------------
# USER MUST SET THESE VARIABLES FOR INSTALLATION OF THIS SCRIPT
# USER MUST SET THESE VARIABLES FOR INSTALLATION OF THIS SCRIPT
# USER MUST SET THESE VARIABLES FOR INSTALLATION OF THIS SCRIPT
#

junkdirname="$HOME/.rmfiles"  # name of user junk file subdir
rmlogfile="rmlogfile"         # list of removed files

# bolding/underlining for error messages and for keywords in the
# man page...

b_on='<esc>&dB'    # set bolding on for HP terminal
b_off='<esc>&d@'   # set bolding off for HP terminal

u_on='<esc>&dD'    # set underlining on for HP terminal
u_off='<esc>&d@'   # set underlining off for HP terminal

                # For other types of terminals, substitute appropo
                # terminal char sequences or just set b_on, b_off,
                # u_on, u_off to empty ('').

#-------------------------------------------------------------------
# Initialize some global vars
#
PATH=/bin:/usr/bin:/usr/contrib/bin:/usr/local/bin:/etc:.
TZ=PST8PDT; export TZ  # set time zone for invocations of cmds like date, etc.
options=
export options
fname=`basename $0`
cap_fname=`echo $fname | tr "[a-z]" "[A-Z]"`  # make filename uppercase
cmdline=$*    # used in help-checking later on

                     # default settings:
overwrite='false'    # force overwriting; don't prompt for user verify? 
interactive='false'  # user wants to verify copy for each source file?
removedir='false'    # user wants to remove a subdir?  default = no.


#-------------------------------------------------------------------
# Set up some traps for signals.  NOTE that any shell variables used
#    in the trap routine must have been defined previous to this trap
#    declaration.
#
trap "echo $cap_fname:  Type  $fname -h\<ret\>  for help.'; exit 2" \
      1 2 3 6 7 15


#-------------------------------------------------------------------
# Grab args from command line
#
set -- `getopt ifrh $*`  # check if a valid command line entered
if [ $? -ne 0 ]
then
   echo "$b_on $cap_fname: Bad runstring option.$b_off"
   echo "$b_on    Type  $fname -h<ret> for help.$b_off " 
   exit 2
fi


#-------------------------------------------------------------------
# Parse command args
#
help_wanted=false 

while [ $# -gt 0 ]
do
   case $1 in
   -i)  interactive='true';;
   -f)  ;;
   -r)  removedir='true';;
   -h)  help_wanted='true';;
   --)  shift; break;; # end of options in command line;
                       # source/target names should remain in runstring
   *)   echo "$b_on $cap_fname: Bad runstring option.$b_off "
        echo "$b_on    Type  $fname -h<ret> for help.$b_off " 
        exit 2;;
   esac
   shift
done


#-------------------------------------------------------------------
# Does user want a help screen?
#
if [ -z "$cmdline" -o "$help_wanted" = 'true' ]
then
   more <<!

   $b_on NAME$b_off 
        $cap_fname  --  Safer rm command

   $b_on SYNOPSIS$b_off 
        $b_on $fname  [-i] [-f] [-r <subdir>]  <file> ... $b_off 
        $b_on $fname  -h$b_off 

   $b_on DESCRIPTION$b_off 
        A safer rm command.  Will "delete" files/subdirs and move
        them to a user "junk file" subdirectory.  The current junk dir
        name being used is  $b_on $junkdirname $b_off .  This junk dir
        name is hardcoded into this script.  This script assumes that
        the junk dir already exists before calling this script.

        The $u_on arguments$u_off  are:
   
           $b_on <-i>$b_off            will cause  $fname  to obtain authorization
                          from the user before removing each source file.
   
           $b_on <-f>$b_off            does absolutely nothing and is only accepted
                          for compatibility with the real rm's runstring
                          options.
   
           $b_on <-r>$b_off            will cause  $fname  to remove the subdir
                          specified in the runstring.  The subdir tree
                          structure is moved to the junk file subdir intact
                          before the source subdir is removed.
   
           $b_on <file>  ...$b_off    are the names of the files/dirs to delete.

           $b_on -h$b_off              will cause this help screen to be displayed.
                          This is the same as typing  $fname  <ret>
                          with no parameters.
  

        Files deleted can be unremoved by the user; the unremoval must
        be done by hand (sorry).  This is because the user might have
        removed several files with the same name and the user would have
        to go into each file anyway to figure out which is the file that
        they want to unremove.

        When a file/dir is moved to the junk directory, it is renamed
        with a unique filename so that future rm'ing of files with the
        same name will not cause files in the junk directory to be
        overwritten.

        However, if all of the removed files in the junk directory have
        unique, non-user-friendly names, then the user will have a heck
        of a time trying to figure out which file is the one they want
        if they want to restore a file.

        So, before files are renamed during the remove process, their
        original name and their new unique name will be placed as an entry
        in a listing file, $b_on $junkdirname/$rmlogfile $b_off , along
        with a prefix, 'f' or 'd', to indicate whether the entry is a
        removed file or directory, respectively.

        Note that once a file/dir has been unremoved, its entry in the
        listing file still exists.  The user must remove their entry
        from this listing by hand (sorry again).


   $b_on WARNING$b_off
        This script is not portable because of the use of HP terminal
        escape sequences in the man page and error messages.  You must
        alter script shell vars to make this portable.

   $b_on AUTHOR$b_off
        HP R&D Lab.

!
   exit 0
fi

#
#----------------------------------------------------------------------
#  Do the main loop

while [ $# -gt 0 ]
do
   if [ "$interactive" = 'true' ]
   then
      echo "Remove  $1? (y/n/a[bort]) \c" 
      read answer

      if [ "$answer" = 'a' ]
      then
         exit 0
      fi

      if [ "$answer" != 'y' -a "$answer" != 'Y' ]
      then
         shift   # go on to next source file name
         continue
      fi
   fi

   if [ -d "$1" ]
   then
      if [ $removedir != 'true' ]
      then
	 echo "$b_on $cap_fname: Specified directory removal of  $1$b_off "
	 echo "$b_on    but remove-dir option (-r) not specified.$b_off "
	 echo "$b_on    Type  $fname -h<ret> for help.$b_off " 
         shift
         continue  # bypass this name; go to the next one
      fi
      # create a subdir inside the junk file subdir in which to
      #   deposit "removed" subdir file(s)
      #
      rmname="`expr substr $LOGNAME 1 4``date +%m%d%H%M%S`"
      mkdir $junkdirname/$rmname
      touch "$junkdirname/$rmname"  # give file the current time to aid in
                             # cleaning up the junk dir using this timestamp
      cp -r $1 $junkdirname/$rmname
      /bin/rm -r $1
      echo "d	$1		$rmname" >> $junkdirname/$rmlogfile
   else
      rmname="`expr substr $LOGNAME 1 4``date +%m%d%H%M%S`"
      /bin/mv $1 "$junkdirname/$rmname"
      touch "$junkdirname/$rmname"  # give file the current time to aid in
                             # cleaning up the junk dir using this timestamp
      if [ $? != 0 ]
      then
	 echo "$b_on $cap_fname: Cannot access file/subdir  $1  .$b_off "
	 echo "$b_on    Consult man pages or$b_off " 
	 echo "$b_on    type  $fname -h<ret> for help.$b_off " 
         exit 1
      fi
      echo "f	$1		$rmname" >> $junkdirname/$rmlogfile
   fi

   shift
done 

exit 0  # removal complete


#######################################################################
# IMPLEMENTATION NOTES/ENHANCEMENT REMINDERS:
#
# 1. I was giving some consideration into making this a C program so that
#    it would run faster.  Currently, it is too slow for my tastes.
#
#    Note that one problem in making this thing run faster would be in
#    timestamping the files uniquely, especially if you are copying/renaming
#    several short files all at the same time.  The system clock would
#    not have enough opportunity to change to a unique set of digits.
#
# 2. Instead of uniquely identifying duplicately-named removed files via
#    timestamping in the filename, how about keeping the names the same
#    as the originals as much as possible but adding number suffixes if
#    removing duplicately-named files.  This eliminates the need for
#    an rmlogfile to map timestamped names to real names.
#
# 3. We give the removed files the current mod timestamp so the files'
#    mod timestamp could be used by some sort of cleanup script to do
#    periodic cleanup of the junk dir.  Maybe the cleanup script could
#    be run during login.
#
# 4. If I had more time, I could develop an 'unrm' command script that,
#    when invoked with the name of the file/dir to unremove, would
#    look in the removed-file listing and display all entries matching
#    the user-specified filename.  We could provide unique sequence
#    numbers for each entry, so that the user, after seeing the display
#    of candidate entries, could specify the index number of the
#    entry to unremove.
#
#    This script would remove the entry from the listing after
#    after completion of the removal of the file/dir itself.
#
#    The other problem that must be overcome is that even if we display
#    the candidate list entries to the user for unremoval, the user still
#    may not be able to tell which file is which if there are several
#    entries with the same original file name.  The fix for this would
#    be to allow the user during rm'ing to specify a comment to be added
#    to the list file entry for that file.  When the user asks to
#    unremove a file and is shown the list entries of possible candidates,
#    the user will also see the comment field for each entry.
#



More information about the Comp.unix.questions mailing list