'goto'-substitution in bash-scripts

A question for the experts: In my scripts it occasionally happens that I need a conditional (if ... then) jump to the beginning of the script. In 'Basic' I would have used the frowned upon "goto" script-start; but how do I realize this in my script? There are certainly better ways.

#! /bin/bash

start() {
echo "Welcome to 'my script'"
}

function1() {
code1
}

function2() {
code2
}

critical_funktion() {
if [[ condition... ]]; then
   echo " ... "
fi
}

function3() {
code3
}

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# main program
start

function1
# evaluation function1

function2
# evaluation function2

critical_funktion
# evaluation critical_funktion
if [ $? -eq 0 ]; then
	echo "all right"
else
	**goto start ???**
fi

function3
# evaluation function3

exit 0

Hi graulwolf,

Yes, there are certainly better ways. I'll show one, I determine better. The method is to put the part that is repeated into a loop.

An example:

while :; do
     start
     function1
     function2

     # if critical_function returns zero -> break out of the loop. 
     # else: repeat the loop
     critical_function && break
done 

# continue further with the program
function3 

exit 0

If the block within the loop is too big, you can move the block into another function and just call that function in the loop.

Regards,
joker

2 Likes

I think you're talking about continue and break statements.

1 Like

@joker - fyi - you missed the invocation of function3 as per the requestors initial post, you may want to modify your examples logical accordingly.

I guess a 'while-loop construct' is the right way to the solution. Based on joker's idea :smile: I've written a mini test-script, which runs exactly as expected, with the following script flow:

#!/bin/bash

start() {
	echo "Welcome to 'my script'"
	echo ""
}

function1() {
	echo "here runs function1"
	echo ""
}

function2() {
	echo "here runs function2"
	echo ""
}

critical_function() {
	echo "here runs critical_function"
	read -p "Input 2 variables: " var1 var2
	if [ "$var1" == "$var2" ]; then
		tag=0
		echo "critical_function: all right"
	else
		tag=1
		echo "critical_function: input error"
	fi
}

function3() {
	echo "here runs function3"
}

# continue further with the program function3

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# main program

while : ; do
	start
	function1
	function2
	# if critical_function returns zero -> break out of the loop. 
	# else: repeat the loop
	critical_function
		if [[ "${tag}" -ne 0 ]]; then
		continue
		fi
		break
done

function3

exit 0

Besides using loops instead of goto another basic advice in programming is to avoid global variables whenever possible, because of increased risk of interference of different program parts.

I have another improvement suggestion for your program, keeping the program flow as you introduced it, using the exit code of a function and a local variable with a visibility scope only within a function.

critical_function() {

    # declare the variable named "tag" as local 
    # only accessible within this function
    local tag

	echo "here runs critical_function"
	read -p "Input 2 variables: " var1 var2
	if [ "$var1" == "$var2" ]; then
		tag=0
		echo "critical_function: all right"
	else
		tag=1
		echo "critical_function: input error"
	fi
    return $tag
}

# main program

while : ; do
	start
	function1
	function2
	# if critical_function returns zero -> break out of the loop. 
	# else: repeat the loop
	critical_function && break
done
1 Like

Yes, that's it - just tested and what shall I say "perfect!"
Thank you very much for the improvement. :hugs:

  1. optimization: negate the condition, so continue is last - and can be omitted.
    critical_function
    if [[ "${tag}" -eq 0 ]]; then break; fi
    # the following "done" continues anyway
  1. optimization: use exit status
    return $tag

and

    critical_function
    if [ $? -eq 0 ]; then break; fi
  1. optimization: directly take the exit status
    if critical_function; then break; fi
  1. can be shortened using a logical AND that skips the second command if the first command is false(bad/nonzero exit status).
   critical_function && break
1 Like

Thank you for the further optimizations (Vielen Dank für die weiteren Optimierungen) :hugs:

Just wanted to explain. It ends up with @joker 's solution.

Ok, all clear. Your explanation was more related to my 1ˢᵗ trial; anyway now I'm focusing on joker's modified script. Thanks again. :smile:
btw: My question may be marked as 'solved'.