Another bash script

Discussion in 'Mac Programming' started by SRossi, Dec 5, 2009.

  1. SRossi macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #1
    Hey all,

    Doing another script and wanted to test it even although I haven't finished it yet. Giving me the error:
    Code:
    work OS.sh: line 180: syntax error near unexpected token `;;'
    work OS.sh: line 180: `;;'
    
    Right I understand that its near the ;; at the end of '1') for the case statement, when i take away the esac from the nested case it gives the same error, all that I could think was that it didn't like the nested case. But I'm really unsure whats wrong.

    Could someone have a look a a show me where my stupidness is please. :)

    Code:
    #!/bin/bash
    # Stephen Ross' Backup Script
    
    # Declaring the variables and the path names
    BackupWp = /Users/rosstephen/Documents/wp/Archive_$(date '+%d.%m.%y:%H:%M').tgz
    BackupSs = /Users/rosstephen/Documents/ss/Archive_$(date '+%d.%m.%y:%H:%M').tgz
    BackupPic = /Users/rosstephen/Documents/pics/Archive_$(date '+%d.%m.%y:%H:%M').tgz
    
    Restore = /Users/rosstephen/Documents
    
    # Begin while statement
    while true;
    do
    
    # User choice menu - Main Menu
    
    	clear
    	echo ****************************************************************
    	echo 				Backup and Restore
    	echo ****************************************************************
    	echo Please select an option from 1-3:
    	echo
    	echo "1) Create a backup"
    	echo
    	echo "2) Restore a file from a backup"
    	echo
    	echo "3) Exit system"
    	echo
    
    # Read user input
    read USERCHOICE
    
    # Begin the case statement
    case $USERCHOICE in
    
    
    '1')	# Begin another while statement
    	while true;
    	do
    	
    	# User choice menu - Backup Menu
    
    	clear
    	echo ******************************************************************
    	echo					Backup
    	echo ******************************************************************
    	echo Please select an option 1-4:
    	echo
    	echo "a) Backup text document"
    	echo
    	echo "b) Backup spreadsheet document"
    	echo
    	echo "c) Backup picture"
    	echo
    	echo "d) Return to Main Menu"
    	echo
    	
    	# Read user input
    	read USERCHOICE2
    	
    	# Begin the next case statement
    	case $USERCHOICE2 in
    
    	'a')	clear
    		echo Backup of Text Document
    		sleep 2
    		
    		# Ask user for the pathname to the file they want to backup
    		echo Please enter the complete pathname of the file that you would like to backup.
    		echo Remember you are in:
    		pwd
    
    		# Read in the filename
    		read FILETEXT
    		
    		# If statement to make sure the file is available and is a file not a directory
    		# Otherwise give an error then ask again for a path
    		if [ -f "$FILETEXT" ]; then
    			echo You are going to backup the selected file:
    			echo "$FILETEXT"
    			echo The file will be backed up to the "wp" folder
    			sleep 3
    		else
    			echo That is an incorrect path,
    			echo please check the path and file,
    			echo then enter the pathname again
    			read FILETEXT
    		fi
    
    		# Execute the backup with arguments czf
    		tar -cf $BackupWp $FILETEXT
    
    		# If statement to see if the tar statement failed or succeded
    		# If failed give an error and return to Backup Menu
    		if [ $? == 0]; then
    			echo Backup has been successful		
    			sleep 3
    		else
    			echo Your backup could not be completed, a problem occured..
    			sleep 3
    		fi
    		;;
    
    	'b')	clear
    		echo Backup of Spreadsheet Document
    		sleep 2
    
    		echo Please enter the complete pathname of the file that you would like to backup.
    		echo Remember you are in:
    		pwd
    
    		read FILESPREAD
    
    		if [ -f "$FILESPREAD" ]; then
    			echo You are going to backup the selected file:
    			echo "$FILESPREAD"
    			echo The file will be backed up to the "ss" folder
    			sleep 3
    		else
    			echo That is an incorrect path,
    			echo please check the path and file,
    			echo then enter the pathname again
    			read FILESPREAD
    		fi
    
    		tar -czf $BackupSs $FILESPREAD
    
    		if [ $? == 0 ]; then
    			echo Backup has been successful
    			sleep 3
    		else
    			echo Your backup could not be completed, a problem occured..
    			sleep 3
    		fi
    		;;
    
    	'd')	clear
    		echo Backup of Picture
    		sleep 2
    
    		echo Please enter the complete pathname of the file that you would like to backup.
    		echo Remember you are in:
    		pwd
    
    		read FILEPIC
    
    		if [ -f "$FILEPIC" ]; then
    			echo You are going to backup the selected file:
    			echo "$FILEPIC"
    			echo The file will be backed up to the "pics" folder
    			sleep 3
    		else
    			echo That is an incorrect path,
    			echo please check the path and file,
    			echo then enter the pathname again
    			read FILEPIC
    		fi
    
    		tar -czf $BackupPic $FILEPIC
    
    		if [ $? == 0]; then
    			echo Backup has been successful
    			sleep 3
    		else
    			echo Your backup could not be completed, a problem occured..
    			sleep 3
    		fi
    		;;
    
    	'c')	clear
    		;;
    
    	*)	clear 
    		echo Please try again select 1, 2, 3 or 4 only
    		echo Press enter to continue
    		read -n 1
    		;;
    	
    
    ;;
    
    '2')	clear 
    	echo ******************************************************************
    	echo				Restore
    	echo ******************************************************************
    	echo
    	echo 
    
    ;;
    
    *)
    
    ;;
    
    Thanks again :)

    Stephen
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    You have this:
    Code:
    # Begin while statement
    while true;
    do
    
    and this:
    Code:
    '1')	# Begin another while statement
    	while true;
    	do
    
    but no 'done' statements. There may be other unmatched loops or conditionals. Those are just the ones I saw easily.

    You would benefit from using shell functions. It would modularize things a lot better.
     
  3. SRossi thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #3
    Got the first fault fixed now. Thanks chown :) was appreciated.

    The script now runs quite well except when I go to create the backup it gives me the error:

    That i must remove the leading / from the member file.

    But if I do that then my test to see if it is a file fails because it needs the leading /. Is there any way to solve this? I should say the tar command still completes though.

    And about using functions, I would rather just keep it as simple as possible just now until after I have it working then ill change it.

    Thanks,

    Stephen
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    You should read the man page for 'tar'. Carefully. Then work out what this means:
    Code:
    tar -czf "$TAROUT" -C / ./"$ABSPATHNAME"
    
    Also, your invocations of the tar command don't quote the variables. This could cause problems if any value has embedded spaces or other things that might be misinterpreted by the shell.


    Your use of tar may lose data. I don't know if tar preserves resource-forks, Finder metadata, and extended attributes. These may be important (or even critical) to recovering the original file. I recommend the 'ditto' command instead.


    My purpose in suggesting functions is that it might help you make it work sooner by reducing the amount of code you have to write and test. The code would be simpler and a lot smaller if you defined a few functions.

    Right now, there's a lot of duplicated code (very similar, or even identical), and it's hard to see the overall structure. If you don't know how to define functions, this is a good opportunity to learn. It's really not that hard, and it pays off pretty easily.


    You have a bug whenever your first prompt for a file to backup is not a file. The second input is always accepted, even if it isn't a file or doesn't exist. You need a loop rather than a simple if/else (loop until the input is a file or user cancels).

    This is also a bug:
    Code:
    	echo "c) Backup picture"
    	echo
    	echo "d) Return to Main Menu"
    
    Code:
    	'd')	clear
    		echo Backup of Picture
    ...		;;
    
    	'c')	clear
    		;;
    
     
  5. SRossi thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #5
    Thanks chown, i have changed my script into different functions and it does look surprisingly cleaner.

    But the only thing is now, in my new function i pass in the choice selected and if it is a certain value I want it to return to the main code and then out of the case statement, this works.

    But I also want to return out of the function if the user wishes to cancel, the way I am doing this just now is:

    Code:
    if [ "$1" == "d" ]; then
    return 1
    
    ...
    
    if [ $INPUT == "quit" ]; then
    return 3
    and in the main function I have after the call to my other function:

    Code:
    if [ "$?" -eq "1" ]; then
    		clear
    		x=1
    	elif [ "$?" -eq "2" ]; then
    		clear
    		echo You must select between a, b, c and d
    		echo Please try again
    		sleep 2	
    	elif [ "$?" -eq "3" ]; then
    		clear
    		echo You canceled the backup
    		echo You will return to the backup menu
    		sleep 2
    	else
    		echo Backup Succeeded
    		sleep 2	
    	fi
    
    The thing is if the user quits e.g. $? to be 1 then it works fine but with the other returns it always does the else part but never the elif part it should. I looked on google before posting and my way seems as though it is the correct way, is there perhaps something I am doing wrong or am I just not understanding function returns in bash scripts?

    Thanks,

    Stephen
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    Post all your code. Without all the code, there's not enough context to debug it.


    $? is always the exit status of the most recent command. Every command, builtin, or function affects the value of $?. Every 'echo' and 'sleep' and so on changes its value. Even the [ that tests the value affects $? (read 'man test'). For example, contrast the following outputs:

    Code:
    echo $?; [ "" ] ; echo $?
    echo $?; [ "x" ]; echo $?
    
    If you intend to refer to a specific value of $? at a specific point in time, e.g. immediately after your function returns, then you need to assign $? to a specific variable:
    Code:
    funStatus ="$?"
    
    then use "$funStatus" instead of "$?" to evaluate the different status values returned by the function.
     
  7. SRossi thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #7
    Heres my code, all works except the returns:

    Code:
    #!/bin/bash
    # Stephen Ross's Backup Script
    
    RestoreTo=/Users/rosstephen/Documents/
    
    Backup()
    {
    	if [ "$1" == "a" ]; then
    		TAROUT="/Users/rosstephen/Documents/wp/Archive_$(date '+%d.%m.%y:%H:%M').tgz"
    	elif [ "$1" == "b" ]; then
    		TAROUT="/Users/rosstephen/Documents/ss/Archive_$(date '+%d.%m.%y:%H:%M').tgz"
    	elif [ "$1" == "c" ]; then
    		TAROUT="/Users/rosstephen/Documents/pics/Archive_$(date '+%d.%m.%y:%H:%M').tgz"
    	elif [ "$1" == "d" ]; then
    		return 1
    	else
    		return 2
    	fi
    
    	clear
    
    	x=0	
    
    	while [ "$x" -eq "0" ]; do
    
    	echo Please enter the complete pathname of the file that you would like to backup.
    	echo Remember to add /username/documents at the start
    
    	read FILETEXT
    
    	if [ $FILETEXT == "quit" ]; then
    		return 3
    	fi
    
    	if [ -f $FILETEXT ]; then
    		echo You are going to backup the selected file:
    		echo "$FILETEXT"
    		x=1
    		sleep 3
    	else
    		echo That is an incorrect path,
    		echo please check the path and file,
    		echo then enter the pathname again
    		sleep 2
    		clear
    	fi
    
    	done
    
    	clear
    	echo The backup will now proceed
    	tar -czf "$TAROUT" -C / ./"$FILETEXT"
    	
    	# If statement to see if the tar statement failed or succeded
    	# If failed give an error and return to Backup Menu
    	if [ $? == 0 ]; then
    		echo Backup has been successful		
    		sleep 3
    	else
    		echo Your backup could not be completed, a problem occured..
    		sleep 3
    	fi
    
    	return 0
    }
    
    Restore()
    {
    	if [ "$1" == "a" ]; then
    		RestorePath="/Users/rosstephen/Documents/wp/"
    		cd /Users/rosstephen/Documents/wp
    	elif [ "$1" == "b" ]; then
    		RestorePath="/Users/rosstephen/Documents/ss/"
    		cd /Users/rosstephen/Documents/ss
    	elif [ "$1" == "c" ]; then
    		RestorePath="/Users/rosstephen/Documents/pics/"
    		cd /Users/rosstephen/Documents/pics
    	elif [ "$1" == "d" ]; then
    		return 1
    	else
    		return 2
    	fi
    
    	clear
    	echo You are about to restore a file from a backup
    	sleep 2
    
    	x=0	
    
    	while [ "$x" -eq "0" ]; do
    
    	echo Listed below are any backups that are in the backup folder
    	ls $RestorePath | grep *.tgz
    	echo
    	echo Please select the file you would like to restore
    
    	read FILE
    
    	if [ $FILE == "quit" ]; then
    		return 3
    	fi
    
    	if [ -f $FILE ]; then
    		echo You are going to restore the selected file:
    		echo "$FILE"
    		x=1
    		sleep 3
    	else
    		echo That is an incorrect path,
    		echo please check the path and file,
    		echo then enter the pathname again
    		sleep 2
    		clear
    	fi
    	
    	done
    
    	Restore=$RestorePath$FILE
    	echo $Restore
    	sleep 3
    
    	clear 
    	echo The file will now be restored
    	
    	tar -xzf "$Restore"
    
    	if [ $? == 0 ]; then
    		echo "$FILE" has been restored
    		sleep 2
    	else
    		echo There was an error during the restore
    		sleep 2
    	fi
    
    	return 0
    }
    
    
    # Begin while statement
    while true;
    do
    
    # User choice menu - Main Menu
    
    	clear
    	echo ----------------------------------------------------------------
    	echo 				Backup and Restore
    	echo ----------------------------------------------------------------
    	echo Please select an option from 1-3:
    	echo
    	echo "1) Create a backup"
    	echo
    	echo "2) Restore a file from a backup"
    	echo
    	echo "3) Exit system"
    	echo
    
    # Read user input
    read USERCHOICE
    
    # Begin the case statement
    case $USERCHOICE in
    
    
    '1')	x=0
    	# Begin another while statement
    	while [ "$x" -eq "0" ];
    	do
    	
    	# User choice menu - Backup Menu
    
    	clear
    	echo ------------------------------------------------------------------ 
    	echo                           Backup
    	echo ------------------------------------------------------------------
    	echo Please select an option 1-4:
    	echo
    	echo "a) Backup text document"
    	echo
    	echo "b) Backup spreadsheet document"
    	echo
    	echo "c) Backup picture"
    	echo
    	echo "d) Return to Main Menu"
    	echo
    	
    	# Read user input
    	read USERCHOICE2
    	
    	clear
    
    	Backup $USERCHOICE2
    
    	if [ "$?" -eq "1" ]; then
    		clear
    		x=1
    	elif [ "$?" -eq "2" ]; then
    		clear
    		echo You must select between a, b, c and d
    		echo Please try again
    		sleep 2	
    	elif [ "$?" -eq "3" ]; then
    		clear
    		echo You canceled the backup
    		echo You will return to the backup menu
    		sleep 2
    	else
    		echo Backup Succeeded
    		sleep 2	
    	fi
    
    	done
    ;;
    
    '2')	x=0
    	# Begin another while statement
    	while [ "$x" -eq "0" ];
    	do
    	
    	# USer choice menu - Restore Menu
    
    	clear
    	echo ------------------------------------------------------------------ 
    	echo                           Restore
    	echo ------------------------------------------------------------------
    	echo Please select an option 1-4:
    	echo
    	echo "a) Restore text document"
    	echo
    	echo "b) Restore spreadsheet document"
    	echo
    	echo "c) Restore picture"
    	echo
    	echo "d) Return to Main Menu"
    	echo
    	
    	# Read user input
    	read USERCHOICE2
    	
    	clear
    
    	Restore $USERCHOICE2
    
    	if [ "$?" -eq "1" ]; then
    		clear
    		x=1
    	elif [ "$?" -eq "2" ]; then
    		clear
    		echo You must select between a, b, c and d
    		echo Please try again
    		sleep 2	
    	elif [ "$?" -eq "3" ]; then
    		clear
    		echo You canceled the restore
    		echo You will return to the restore menu
    		sleep 2
    	else
    		echo Restore Succeeded
    		sleep 2	
    	fi
    
    	done
    ;;
    
    '3')	clear
    	break
    ;;
    
    *)	clear
    	echo Sorry try again, select 1, 2 or 3 only
    	echo press enter to continue
    	read -n 1
    
    ;;
    esac
    done
    
    exit
    And so if i was to do:

    Code:
    return_val="$?"
    return return_val
    And then done the if statement on the value of that it should work?

    And finally starting to understand scripting a but better thanks :)

    Stephen
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    I don't know what "And then done the if statement" means. There are several 'done' and 'if' statements.

    You should be able to think this through and work it out yourself. If you can't think it through, then run an experiment and see what happens.

    First, it would have to be 'return $return_val', for what should be obvious reasons.

    Second, where are you suggesting that your code sequence be placed? If you mean "at the end of the Backup function", then think that through and work out what effect it should have.

    This is where I meant:
    Code:
    	Backup "$USERCHOICE2"
    	[B]return_val="$?"[/B]
    
    	if [ [B]"$return_val" -eq "1"[/B] ]; then
    		clear
    		x=1
    	elif [ [B]"$return_val" -eq "2"[/B] ]; then
            [I]...etc...[/I]
    
    Note that I have quoted the parameter to the Backup function for safety. You have other places where unquoted parameters are used. You need to get in the habit of quoting things when needed or your scripts may become dangerous if a parameter containing a space is used.

    You could also use a 'case' statement instead of an if/elif chain. This would remove the need for a separate return_val variable. In fact, many of your if/elif chains could be done by 'case' statements.
     
  9. SRossi thread starter macrumors regular

    Joined:
    May 27, 2009
    Location:
    Glasgow, Scotland
    #9
    Thanks Chown for all your help, if there is anything you need help with in the future I would be glad to return the favour.

    Stephen
     

Share This Page