SOLVED bash script via web interface vs nas shell "Missing {.}"

Status
Not open for further replies.

ghostwolf59

Contributor
Joined
Mar 2, 2013
Messages
165
Have this weird thing happening - the bash script I created works perfect via the web interface - the bash used then is /usr/local/bin/bash
Then tried to run the same script from of a the shell on my nas - when executing the same script I get "Missing }."
Figured that there's a different bash when open a shell on nas. and sure enough - the bash shell now is /bin/bash
Updated the script to point to this, but the same "Missing }." is thrown. Any idea why this happens...?

Also wonder how I can get this script onto my thumb drive so it's always available - as it stands now I copied it to one of my mounted volumes (which is silly)
here's the complete script as it stands (supports rsync as well as zfs snapshot backup/restore)

Code:
#/usr/local/bin/bash
#
# backup/restore syncronization
#
# https://forums.freenas.org/index.php?threads/zfs-send-to-external-backup-drive.17850/

#echo "Starting"

req="${1,,}" #convert to lower case request (backup or restore)
drive="${2^^}" #convert to upper case target/destination drive
dt="${3^^}" #convert to upper case date of a backup point to resore from
force="${3,,}" #convert flag setting ty lower case
u="" #default force setting flag (rsync)

if [ "${force}" == "" ];
then
   force=""
fi

back="BACKUP" #default name of backup volume

status=0

# run if user hits control-c
function control_c() {
  echo -en "\n*** Ouch! Exiting ***\n"
  exit $?
}

# trap keyboard interrupt (control-c)
trap control_c SIGINT

#pause prompt
function pause(){
  read -p "$*"
}



#command line syntax error message
function error() {
   echo "command line error ${@}"
   echo " to backup zfs with snapshots:"
   echo "  /mnt/<drive>/backrest.sh backup <drive>"
   echo " or to restore zfs with snapshots:"
   echo "  /mnt/<drive>/backrest.sh restore <drive> <ccyymmdd> or sync <drive>"
   echo " or to do rsync do ..."
   echo "  backup drive to /mnt/${back}:"
   echo "  /mnt/<drive>/backrest.sh sync <drive>"
   echo "  restore from /mnt/${back}:"
   echo "  /mnt/<drive>/backrest.sh syncrestore <drive>"
   return
}

#check if the drive is mounted or even exists on the system
function is_mounted() {
   local FS=$(zfs get -H mounted "${@}")
   FS_REGEX="^${@}\s+mounted\s+yes\s+-"

   #check for errors
   if [ "${FS}" == "" ]; then
     status=$? #status from last function call
     echo "${@} not found/mounted"
     return 0 #evaluates to false - error condition
   fi
   if [[ $FS =~ $FS_REGEX ]]; then
     status=$? #status from last function call
     echo "${@} not mounted"
     return 0 #evaluates to false - error condition
   fi
 
   #drive checked out ok
   echo "${@} mounted"
   return 1 #evaluates to true - success
}

#wrapper to ensure BACKUP drive and nominated target/destination drive is mounted/exists
function checkMountPoints() {

   #checks if the backup/restore drive exists
 
   #check source drive
   is_mounted ${@}
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     echo "${@} checked out ok"
   else 
     return 0 #evalutes to false error condition
   fi
 
   #check backup drive
   is_mounted ${back}
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     echo "${back} checked out ok"
     return 1
   else 
     return 0 #evaluates to false - error condition
   fi
}


#rsync across the whole source volume to backup destination volume
function RyncFromSourceToDetachedMediaBackup() {
   #checkMountPoints ${drive}
   checkMountPoints ${1}
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     echo "rsync -pogavt${2} /mnt/${1} /mnt/${back}"
     rsync -pogavt${2} /mnt/${1} /mnt/${back}
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "rsync from /mnt/${1} to /mnt/${back}/${1} successful"
     else
       echo "rsync from /mnt/${1} to /mnt/${back}/${1} unsuccessful"
     fi
   else
     status=1
   fi
   return $status
}

#rsync across the whole backup volume to destination source volumne
function RsyncFromBackupToSourceRestore() {
   #checkMountPoints ${drive}
   checkMountPoints ${1}
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     echo "rsync -pogavt${2} /mnt/${back}/${1}/ /mnt/${1}"
     rsync -pogavt${2} /mnt/${back}/${1}/ /mnt/${1}
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "rsync from /mnt/${back}/${1}/ to /mnt/${1} successful"
     else
       echo "rsync from /mnt/${back}/${1}/ to /mnt/${1} unsuccessful"
     fi
   else
     status=1
   fi
   return $status
}


function takesnapshot() {
   #echo "start takesnapshot() of ${@} as ${@}-$(date +%Y%m%d)"

   zfs snapshot -r ${@}@${@}-$(date +%Y%m%d)
   status=$? #status from last function call
   if [ $status == 0 ];
   then
     status=1
   else
     status=0
   fi 
   #echo "chk outcome takesnapshot() $status"

   return $status
}

function renamesnapshot() {
   #echo "start renamesnapshot() ${@}@${@}-${date} to ${@}@${@}-$(date +%Y%m%d)-restored_$(date +%Y%m%d)"

   zfs rename -r ${@}@${@}-${date} ${@}@${@}-$(date +%Y%m%d)-restored_$(date +%Y%m%d)
   status=$? #status from last function call
   if [ $status == 0 ];
   then
     status=1
   else
     status=0
   fi 

   #echo "chk outcome renamesnapshot() $status"

   return $status
}

function deletesnapshot() {
   #echo "start deletesnapshot() ${@}@${@}-$(date +%Y%m%d)"
   zfs destroy -r ${@}@${@}-$(date +%Y%m%d)
   status=$? #status from last function call
   if [ $status == 0 ];
   then
     status=1
   else
     status=0
   fi 

   #echo "chk outcome deletesnapshot() $status"

   return $status
}

function sendsnapshot () {
   #echo "start sendsnapshot() ${@}@${@}-$(date +%Y%m%d) to ${back}/${@}-$(date +%Y%m%d)"

   zfs send -R ${@}@${@}-$(date +%Y%m%d) | zfs receive -vF ${back}/${@}-$(date +%Y%m%d)
   status=$? #status from last function call
   if [ $status == 0 ];
   then
     status=1
   else
     status=0
   fi 

   #echo "chk outcome sendsnapshot() $status"

   return $status
} 

#takes the backup of nominated drive, sending it to the BACKUP drive
function doBackup() {
   checkMountPoints ${drive}
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     echo "about to take snapshot of $drive"
     takesnapshot $drive
     status=$? #status from last function call
     if [ $status == 1 ];
     then
       echo "about to send snapshot of $drive to $back"
       sendsnapshot ${drive}
       status=$? #status from last function call
       if [ $status == 1 ];
       then
         echo "about to take snapshot of ${back}"
         takesnapshot ${back};
         status=$? #status from last function call
         if [ $status == 1 ];
         then
           echo "about to delete snapshot of ${drive}"
           deletesnapshot ${drive}
           status=$? #status from last function call
           if [ $status == 1 ];
           then
             #echo "about to delete snapshot of ${back}"
             #deletesnapshot ${back}
             #status=$? #status from last function call
             #if [ $status == 1 ];
             #then
               echo "Successfully backed up ${drive} onto ${back}"
               return
             #fi 
           fi 
         fi 
       fi 
     fi 
   fi
   status=$? #status from last function call
   error $status
   echo "Unsuccessful backup of ${@}"
   return
}

function validateDate() {
   # Script expecting a Date parameter in YYYYMMDD format as input
   echo ${dt} | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$'
   status=$? #status from last function call
   if [ $status -eq 0 ]; #valid date
   then
     status=0 #do nothing statement
   else
     #since previous test failed I will try to reverse the argument assuming a reverce input
     echo ${drive} | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$'
     status=$? #status from last function call
     if [ $status -eq 0 ]; #valid date
     then
       # parameters in reversed argument order - switching order
       tmpdrive="${dt}"
       dt="${drive}"
       drive="${tmpdrive}"
     fi 
   fi
 
   return $status #0=success, otherwise error
}

#some checks post restore a zfs volume and all datasets
function doRestore() {
   echo "Start Restore..."
   validateDate
   status=$? #status from last function call
   #validated date params
   if [ $status == 0 ];
   then
     checkMountPoints ${drive} #check if nominated target/destination drive/volume is mounted/available
     status=$? #status from last function call
     if [ $status == 1 ]; #target/destination drives(volumes) accessible
     then
       echo "Initiate restore of $drive from ${back}@${drive}-${dt}"
       execRestore ${drive} ${dt} #do the restore of drive for date (assuming it exists for set date)
       status=$? #status from last function call
       if [ $status == 0 ]; #success
       then
         echo "Successfully restored $drive from ${back}@${drive}-${dt}"
       else 
         echo "Unsuccessful restore of $drive from ${back}"
         error $status
       fi
     else 
       echo "${drive} or ${back} not mounted"
       error $status
     fi 
   else
     echo "${dt} is not a valid date"
     error $status
   fi
 
   return
}

function destroyDataset () {
   echo "about to delete dataset ${@}"
 
   if [ "${@}" == "" ];
   then
     echo "empty dataset passed"
     status=1
   else 
     #zfs destroy -R $back/$drive-$dt/${@}
     zfs destroy -R ${@}
     status=$? #status from last function call
     if [ $status == 0 ]; #successful rename
     then
       #echo "Successfully deleted dataset $back/$drive-$dt/${@}"
       echo "Successfully deleted dataset ${@}"
     else
       #echo "Faailed to delete dataset $back/$drive-$dt/${@}"
       echo "Failed to delete dataset ${@}"
     fi 
   fi

   return $status
}

function jailCleanExists () {
   echo "check if ${@} exists"

   zfs list ${@}
   status=$? #status from last function call
   if [ $status == 0 ]; #successful rename
   then
     status=1
   else
     status=0
   fi

   echo "status from call $status"
   return $status
} 

function removeJailDependencies () {

   exception=0
 
   jailCleanExists ${@}/jails/sickbeard_2
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     destroyDataset ${@}/jails/sickbeard_2
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "successfully removed ${@}/jails/jails/sickbeard_2"
     else
       echo "Warning: ${@}/jails/jails/sickbeard_2 not found"
       exception=1
     fi
   fi 

   jailCleanExists ${@}/jails/plexmediaserver_1
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     destroyDataset ${@}/jails/plexmediaserver_1
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "successfully removed ${@}/jails/plexmediaserver_1"
     else
       echo "Warning: ${@}/jails/plexmediaserver_1 not found"
       exception=1
     fi
   fi
 
   jailCleanExists ${@}/jails/firefly_1
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     destroyDataset ${@}/jails/firefly_1
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "successfully removed ${@}/jails/firefly_1"
     else
       echo "Warning: ${@}/jails/firefly_1 not found"
       exception=1
     fi 
   fi
 
   jailCleanExists ${@}/jails/.warden-template-pluginjail@clean
   status=$? #status from last function call
   if [ $status == 1 ];
   then
     destroyDataset ${@}/jails/.warden-template-pluginjail@clean
     status=$? #status from last function call
     if [ $status == 0 ];
     then
       echo "successfully removed ${@}/jails/.warden-template-pluginjail@clean"
     else
       echo "Warning: ${@}/jails/.warden-template-pluginjail@clean not found"
       exception=1
     fi 
   fi

   status=$exception
   return $status

}

#restore a zfs volume and all datasets
function execRestore(){
   #echo "Restoring ${drive} for ${dt} from ${back}/${drive}-${dt}..."
   #test
   echo "Restoring ${back}@${drive}-${dt} to ${drive}..."
 
   #assume the backup for set date exists - the backup name is based on previous backup whith inherited source volume name
   zfs send -R ${back}@${drive}-${dt} | zfs receive -vF ${drive}/
   status=$? #status from last function call
   if [ $status == 0 ]; #worked
   then
     #take a new snapshot from restored data
     takesnapshot ${drive}
     status=$? #status from last function call
     if [ $status == 0 ]; #snapshot successful
     then
       renamesnapshot ${drive} #rename previous snaphot on the destination volume to prevent accidental restore since the backup release superseeds this snapshot
       status=$? #status from last function call
       if [ $status == 0 ]; #successful rename
       then
         echo "about to delete snapshot of ${back}"
         deletesnapshot ${back} #remove the snapshot from the backup drive since it's now been restored (not sure this is the correct approach)
         status=$? #status from last function call
         if [ $status == 0 ]; #successful rename
         then
           echo "about to delete dataset ${back}@${drive}-${dt}"
           destroyDataset ${back}@${drive}-${dt}
           status=$? #status from last function call
           if [ $status == 0 ]; #successful rename
           then
             echo "check if ${drive}/jails/.warden-template-pluginjail@clean exists"
             jailCleanExists ${drive}/jails/.warden-template-pluginjail@clean
             status=$? #status from last function call
             if [ $status == 1 ]; #@clean exists
             then
               echo "remove dependencies for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
               removeJailDependencies ${back}/${drive}-${dt}
               status=$? #status from last function call
               if [ $status == 0 ]; #@cleaned
               then
                 echo "Dependencies successfully removed for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
                 status=1
               else
                 echo "Exception thrown while cleaning up dependant datasets for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
                 status=0
               fi
             else
               status=1
             fi
           else
             status=0
           fi
         fi
       fi 
     fi
   fi

   return $status   #status from last call resurned (0=error, 1=success)
}


#main logic (backup or restore)
if [ "${drive}" == "" ];
then
   error
else
   if [ "${req}" == "backup" ];
   then
     doBackup $drive
   else
     if [ "${req}" == "restore" ];
     then
       doRestore $drive $dt
     else
       if [ "${req}" == "sync" ];
       then
         if [ "${force}" == "-f" ];
         then
           u="u"
         fi 
         echo "WARNING: You are about to rsync /mnt/${drive} onto /mnt/${back}"
         pause "Press [Enter]key  to continue or CTRL+C to cancel"
         RyncFromSourceToDetachedMediaBackup ${drive} ${u}
         status=$? #status from last function call
         if [ $status == 0 ]; #successful rename
         then
           echo "ok"
         else
           error
         fi
       else
         if [ "${req}" == "syncrestore" ];
         then
           if [ "${force}" == "-f" ];
           then
             u="u"
           fi 
           echo "WARNING: You are about to rsync /mnt/${back}/${drive}/ onto /mnt/${drive}"
           pause "Press [Enter]key  to continue or CTRL+C to cancel"
           RsyncFromBackupToSourceRestore ${drive} ${u}
           status=$? #status from last function call
           if [ $status == 0 ]; #successful rename
           then
             echo "ok"
           else
             error
           fi
         else
           error
         fi 
       fi 
     fi 
   fi
fi
exit 0
#
 
Last edited:

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
Posting your script in code tags would have made it easier to read--put [ code ] (without the spaces) before it and [ /code ] (again, without the spaces) after it. It looks to me, though, like your first line is missing the exclamation--it should be

#!/usr/local/bin/bash

or just

#!/bin/bash
 

ghostwolf59

Contributor
Joined
Mar 2, 2013
Messages
165
Posting your script in code tags would have made it easier to read--put [ code ] (without the spaces) before it and [ /code ] (again, without the spaces) after it. It looks to me, though, like your first line is missing the exclamation--it should be

#!/usr/local/bin/bash

or just

#!/bin/bash


Updated my post as you suggestes - no real difference when it comes to formatting.

Thanks for pointing out the missing exclamation mark though - is this a bug or what - it works just fine via the web interface but not when opening the shell on the nas box itself ?

cheers
 

ghostwolf59

Contributor
Joined
Mar 2, 2013
Messages
165
Updated my post as you suggestes - no real difference when it comes to formatting.

Thanks for pointing out the missing exclamation mark though - is this a bug or what - it works just fine via the web interface but not when opening the shell on the nas box itself ?

cheers


umm the [ code ] [ /code ] should have been [ code ] [ /code ] (witthout spaces - appreciate you trying to point this out but your stuck making this little quirk to allow the [ code ] [ /code ] to appear)
also noted that xml tags don't work with this - anyway, the script have been updated to be easier to read
 
Last edited:

danb35

Hall of Famer
Joined
Aug 16, 2011
Messages
15,504
Adding the code tags makes your post a lot shorter to scroll through. It also preserves spacing better, though that's not a big factor in this case.

When you go to the shell from the web GUI, it uses bash. When you choose option 9, the shell, from the console menu, it uses csh. Different shells, and their syntax is different. The first line in your script, if formatted correctly (i.e., #!/bin/whatevershell) can require any command interpreter you want (a traditional shell, a scripting language like python or perl, whatever).
 

ghostwolf59

Contributor
Joined
Mar 2, 2013
Messages
165
great stuff ! btw how do I update my post from <PROBLEM> to <SOLVED> ? find this quite handy when looking for solutions
 
Status
Not open for further replies.
Top