Having trouble with My Bash Script, need Help debugging

Hello Friends
I am having trouble with my script below. I will describe the problems below the code box. I am hoping that some of the experts here can help me.

#!/bin/bash
#=========================================================================================================
# Rsync File Restore Script [Unfinished]                   ********************************************* #
# This script is in /raid0/data/backup/                    *                                           * #
#                                                          * Author: Johnny J. Davis                   * #
#                                                          * Date Created:                             * #
# [ Last Modified ]                                        * Script name: filerestore_take2.sh         * #
# Date:                    12/18/12                        *            [incomplete]                   * #
# Time:                     9:32 AM                        *                                           * #
#                                                          ********************************************* #                                          
##########################################################################################################

#=========================================================================================================
#                                       ****[Begin Variables]****
#=========================================================================================================
LogDns1=/raid0/data/backup/logs/dns1_file_restore.log
LogServices=/raid0/data/backup/logs/services_file_restore.log
LogInet1=/raid0/data/backup/logs/inet1_file_restore.log
LogUser=/raid0/data/backup/logs/UserLog.log
NoLog="echo No log present for $secondhalf"             # The variable $secondhalf will be assigned later
RSYNC=/usr/bin/rsync                                    # on as a result of user input. 
DATE=/bin/date
ECHO=/bin/echo
USER=root
#---------------------------------------------------------------------------------------------------------
# {End Variables}
#=========================================================================================================
#                                       ****[Begin Functions]****
#=========================================================================================================
# Function to remove previous log, if present (DNS1)
removeLogDns1(){
if [ -a $LogDns1 ]
  then
    rm $LogDns1
  else
    echo "No log present for Dns1!"
fi
}
# Function to remove previous log, if present (SERVICES)
removeLogServices(){
if [ -a $LogServices ]
  then
    rm $LogServices
  else
    echo "No log present for Services!"
fi
}
# Function to remove previous log, if present (INET1)
removeLogInet1(){
if [ -a $LogInet1 ]
  then
    rm $LogInet1
  else
    echo "No log present for Inet1!"
fi
}        
#-------------------------------------------------------------(Step 6)------------------------------------
# Function that confirms the intention to restore and restores the file  
# placed in the variable fileName by the fileCheck function in step 5b.
restoreFile(){
echo -n "Restore $fileName? [Y/N]?> "
read -r answer
 case $answer in
      Y ) echo "Restoring the file $fileName!"                  # Will be Adding additional commands here
          ;;
      N ) echo "Action aborted, nothing restored!"; makeSelection
          ;;
      * ) echo "Must answer with a Y or N!" 
          ;;
 esac
#exit 0; sh /raid0/data/backup/filerestore_take2.sh
} 
#-------------------------------------------------------------(Step 5a)---------------------------
# Function that changes the current working directory to the choice made in step 2, if in fact
# it was a directory.
cdNow(){
if [ -d $choice ]
 then
   cd $choice; echo "Changed Directory to $PWD"; makeSelection
 else                                                  # If the choice from step 2 is not a directory and
   echo "This is not a directory!"                     # manages to make it to this step, the script will     
fi                                                     # terminate with echo.
}
#-------------------------------------------------------------(Step 5b)---------------------------
# Function checks to see if the choice from step 2, is in fact a file. If so, it stores the file
# name in a variable called fileName and proceeds to call the restoreFile function in step 6.
fileCheck(){
if [ -f $choice ]
 then
   fileName=$choice; restoreFile
 else                                                         # If the choice from step 2 is not a file
   echo "This is not a file!"                                 # and manages to make it to this step, the
fi                                                            # script will terminate with echo.
}  
#-----------------------------------------------------------(Step 4)--------------------------------------
# Function that determines if the selection from step 1 is a file 
# or directory and moves to next function based on the result.
checkType(){
while [ -d $choice ]                                   # Checks the variable to see if it is a directory.
  do                                                   # If so, it calls the cdNow function, in step 5a. 
   cdNow
   break;
  done
  
while [ -f $choice ]                                   # Checks the variable to see if it is a file. If so,
  do                                                   # it calls the fileCheck function, in step 5b.
   fileCheck
   break;
  done
}
#------------------------------------------------------------(Step 3)-------------------------------------
# Function checks for existance of files.
anyFiles(){  
ls ./* > /dev/null 2>&1
  if [ "$?" = "0" ]
   then
     checkType
   else
     echo "No files exist here!"
  fi
}
#------------------------------------------------------------(Step 2)-------------------------------------
# Function that allows selecting either a file or directory.
makeSelection(){
PS3="Enter choice [ctrl-c quits]> "
select choice in `for i in $(ls -p ./); do echo ${i%%.*}; done`   
 do echo; break; done
anyFiles
}
#---------------------------------------------------------------------------------------------------------
# {End Functions}
#=========================================================================================================
#                                     ****[Script Start]****  (Step 1)
#=========================================================================================================
#
echo
echo
echo
clear
echo "Current Working Directory: $PWD"       
echo
echo "Please select file or directory."
echo
echo "[Selecton Menu]"
makeSelection

I haven't found a way around using 'ls' or 'find' in my select statement to produce the Selection Menu. I know that they return unsafe IFS results and that it results in some files having multiple menu entries. I am still looking for a way to combat that issue. However, I would like to have someone look at it for me.

Upon execution of the script, I get some prompts duplicated after performing the task. For instance, after traversing several directories, to finally reach the file that I would like to restore. I am prompted to restore the file. I say yes to restore. I get the echo stating that the file is being restored, then immediately after, I get the same prompt. I know it's something in the functions causing this, but I don't know how to resolve it. Also, while entering a selection at the selection menu, I deliberately entered an invalid choice to see what would happen. It drops to /root and echos "No files exist here!" and prompts to Restore ? [Y/N]?>. I don't know what's causing this or how to fix it. Do you have any ideas about any of this?

Please Note, that this script is not complete. I still have much work to do.

I should also point out that I am working on a Thecus N4100Pro Nas via ssh. The box has a very limited command set, as it is running a dumbed-down version of Slackware. Some options for the commands that do exist, are non-existent on this box. For instance, 'find' with the -maxdepth or -mindepth options. Find is a valid command for the box, but the options do not exist.

Thanks for your help.

One way to deal with IFS embedded file names in menus is to present them numbered or with radio buttons or on a button so their IFS characters are not an issue. Layout can be a challenge. Make it a web service, call with lynx or such and let the browser table sort it out. You need to be more strict about using quotes, like "$whatever", to keep the IFS characters under control.

Using while for if deprives you of the else. A function is worthwhile if you call the code twice, but for casual efforts with good internal architecture they are rarely needed. Lots of diuplicate code elements should tell you the architcture is sloppy.

Set the PATH right and trust it, not put full path of commands in similar variables. Do not steal/overload variable names already in use by UNIX, e.g., $USER.

Declared functions are not steps, but where they are called maybe a step.

1 Like

ls -Q might help a little with those IFS characters, if it's implemented in your limited command set.

Probably best to test if a directory is empty before calling select (no point presenting an empty menu). You can trap invalid choices in you select statement something like this:

echo "Select file - or Q to quit"
select file in `ls -Q $mydir`
do
    if [ $REPLY = "Q" ] then
        ACTION=Q 
        break
    fi
    if [ -z "$file" ]
    then
          echo "Invalid choice"
     else
          break
     fi
done
1 Like

@DGPickett and @Chubler XL, thanks for your valuable input. I will definitely heed your advice. I am going to put it to good use immediately.

Thanks again,

-Johnny

Don't overlook the usual '*':

entry_explore(){
 
  for entry in "$entry"/.* "$entry"/*
   do
    case "$entry" in
     (*/.|*/..)
       continue
      ;;
     (*)
       if [ -f "$entry" ]
        then
         echo "Restore file '$entry' ? \c"
         read reply
         case "$reply" in
          ([Yy]*)
            entry_restore
           ;;
          (*)
            # nothing
           ;;
          esac
         continue
        fi
 
       if [ -d "$entry" ]
        then
         entry_explore
        fi
      ;;
     esac
   done
 }
 
entry_restore(){
  # restore one file "$entry"
 }
 
########### MAIN ########
 
for entry in "$@"
 do
  entry_explore
 done

", as the shell preserves the directory entry boundaries from globbing in the '*'

1 Like

Using shopt -s nullglob can also be usefull when testing if any files exist in a directory:

if [-z -n "$(shopt -s nullglob; echo $entry/*)" ]
then
    echo Nothing is directory $entry
fi
1 Like

If DIR/* and DIR/.* resolve to DIR/. DIR/.. and DIR/* literally, the first two are ignored and the last is neither dir nor file. No directory is truly empty, . and .. are real entries with real inode numbers. The nullglob part is nice, but is a subshell more costly than a failed directory lookup? As the man page says, nullglob is so unexpected and potentially destabilizing that you always need to restrict it to a subshell.

1 Like

No . has the same inode number as the directory and .. has the same inode as the parent directory.

$ ls -id .
45056 .
$ mkdir trial_dir
$ ls -id trial_dir
73759 trial_dir
$ cd trial_dir
$ ls -1ia .
73759 .
45056 ..

Yes, two real entries with two real inodes' numbers, unlike meta-symbols like ~ that are expanded by the shell. The inode numbers get a bit funny where there is a mount point, though, and apparently the odd ones are faked by the O/S or file system driver software!