Is there a “goto” statement in bash?

Is there a “goto” statement in bash?



Is there a "goto" statement in bash ? I know It is considered bad practice, but I need specifically "goto".





No, there's not goto in bash (at least it says command not found for me). Why? Chances are there is a better way to do it.
– Niklas B.
Mar 9 '12 at 18:27



goto


command not found





He may have his reasons. I found this question because I want a goto statement to skip over a lot of code for debugging a large script without waiting an hour for various unrelated tasks to complete. I'd certainly not use a goto in the production code, but for debugging my code, it'd make my life infinitely easier, and it'd be easier to spot when it came to remove it.
– Karl Nicoll
Jul 6 '12 at 10:40


goto


goto





@delnan But having no goto can make some things more complicated. There indeed are use cases.
– glglgl
May 15 '13 at 13:56





I'm sick of this goto myth! There's nothing wrong with goto! Everything you write eventually becomes goto. In assembler, there is only goto. A good reason to use goto in higher programming languages is for example jumping out of nested loops in a clean and readable way.
– Alexander Czutro
Jul 3 '14 at 15:07






"Avoid goto" is a great rule. Like any rule, it should be learned in three phases. First: follow the rule until it's second nature. Second, learn to understand the reasons for the rule. Third, learn good exceptions to the rule, based on a comprehensive understanding of how to follow the rule and the reasons for the rule. Avoid skipping steps above, which would be like using "goto". ;-)
– Jeff Learman
Jun 30 '17 at 23:44




11 Answers
11



No, there is not; see §3.2.4 "Compound Commands" in the Bash Reference Manual for information about the control structures that do exist. In particular, note the mention of break and continue, which aren't as flexible as goto, but are more flexible in Bash than in some languages, and may help you achieve what you want. (Whatever it is that you want . . .)


break


continue


goto





Could you expand on "more flexible in Bash than in some languages"?
– user239558
Apr 28 '14 at 9:00





@user239558: Some languages only allow you to break or continue from the innermost loop, whereas Bash lets you specify how many levels of loop to jump. (And even of languages that allow you to break or continue from arbitrary loops, most require that to be expressed statically -- e.g., break foo; will break out of the loop labeled foo -- whereas in Bash it's expressed dynamically -- e.g., break "$foo" will break out of $foo loops.)
– ruakh
Apr 28 '14 at 16:37


break


continue


break


continue


break foo;


foo


break "$foo"


$foo



If you are using it to skip part of a large script for debugging (see Karl Nicoll's comment), then if false could be a good option (not sure if "false" is always available, for me it is in /bin/false):


# ... Code I want to run here ...

if false; then

# ... Code I want to skip here ...

fi

# ... I want to resume here ...



The difficulty comes in when it's time to rip out your debugging code. The "if false" construct is pretty straightforward and memorable, but how do you find the matching fi? If your editor allows you to block indent, you could indent the skipped block (then you'll want to put it back when you're done). Or a comment on the fi line, but it would have to be something you'll remember, which I suspect will be very programmer-dependent.





Yes false is always available. But if you have a block of code you don't want to execute, just comment it out. Or delete it (and look in your source control system if you need to recover it later).
– Keith Thompson
Nov 17 '13 at 15:21


false





If ithe block of code is too long to tediously comment out one line at a time, see these tricks. stackoverflow.com/questions/947897/… However, these don't help a text editor match the beginning to the end, either, so they're not much of an improvement.
– Camille Goudeseune
Jun 21 '16 at 15:48






"if false" is often far better than commenting out code, because it ensures that the enclosed code continues to be legal code. The best excuse for commenting out code is when it really needs to be deleted, but there's something that needs to be remembered -- so it's just a comment, and no longer "code".
– Jeff Learman
Jun 30 '17 at 23:46





I use the comment '#if false;'. That way I can just search for that and find both the beginning and end of the debug removal section.
– Jon
Nov 8 at 20:32



It indeed may be useful for some debug or demonstration needs.



I found that Bob Copeland solution http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:


#!/bin/bash
# include this boilerplate
function jumpto
grep -v ':$')
eval "$cmd"
exit


start=$1:-"start"

jumpto $start

start:
# your script goes here...
x=100
jumpto foo

mid:
x=101
echo "This is not printed!"

foo:
x=$x:-10
echo x is $x



results in:


$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101





"My quest to make bash look like assembly language draws ever nearer to completion." - Wow. Just, wow.
– Brian Agnew
Mar 8 '16 at 16:00





the only thing I'd change is make it so that labels start like so : start: so that they aren't syntax errors.
– Alexej Magura
Apr 7 '17 at 18:03


: start:





Would be better if you did that: cmd=$(sed -n "/#$label:/:a;n;p;ba;" $0 | grep -v ':$') with labels starting as: #start: => this would prevent script errors
– access_granted
Apr 19 '17 at 22:30






Hm, indeed it most likely my mistake. I have edit post. Thank you.
– Hubbitus
Apr 23 at 16:46





set -x helps understand what's going on
– John Lin
Aug 22 at 1:30


set -x



You can use case in bash to simulate a goto:


case


#!/bin/bash

case bar in
foo)
echo foo
;&

bar)
echo bar
;&

*)
echo star
;;
esac



produces:


bar
star





Note that this requires bash v4.0+. It is, however, not a general-purpose goto but a fall-through option for the case statement.
– mklement0
Apr 15 '14 at 18:20


bash v4.0+


goto


case





i think this should be the answer. i have a genuine need for go to in order to support resume execution of a script, from a given instruction. this is, in every way but semantic, goto, and semantics and syntactic sugars are cute, but not strictly necessary. great solution, IMO.
– nathan g
Mar 23 '15 at 14:32





@nathang, whether it's the answer depends on whether your case happens to mesh with the subset of the general case the OP asked about. Unfortunately, the question asks about the general case, making this answer too narrow to be correct. (Whether that question should be closed as too broad for that reason is a different discussion).
– Charles Duffy
Aug 12 '15 at 20:00





goto is more than selecting. goto feature means to be able to jump to places acording to some conditions, creating even a loop like flow...
– Sergio Abreu
Jan 2 '17 at 22:29



If you're testing/debugging a bash script, and simply want to skip forwards past one or more sections of code, here is a very simple way to do it that is also very easy to find and remove later (unlike most of the methods described above).


#!/bin/bash

echo "Run this"

cat >/dev/null <<GOTO_1

echo "Don't run this"

GOTO_1

echo "Also run this"

cat >/dev/null <<GOTO_2

echo "Don't run this either"

GOTO_2

echo "Yet more code I want to run"



To put your script back to normal, just delete any lines with GOTO.


GOTO



We can also prettify this solution, by adding a goto command as an alias:


goto


#!/bin/bash

shopt -s expand_aliases
alias goto="cat >/dev/null <<"

goto GOTO_1

echo "Don't run this"

GOTO_1

echo "Run this"

goto GOTO_2

echo "Don't run this either"

GOTO_2

echo "All done"



Aliases don't usually work in bash scripts, so we need the shopt command to fix that.


shopt



If you want to be able to enable/disable your goto's, we need a little bit more:


goto


#!/bin/bash

shopt -s expand_aliases
if [ -n "$DEBUG" ] ; then
alias goto="cat >/dev/null <<"
else
alias goto=":"
fi

goto '#GOTO_1'

echo "Don't run this"

#GOTO1

echo "Run this"

goto '#GOTO_2'

echo "Don't run this either"

#GOTO_2

echo "All done"



Then you can do export DEBUG=TRUE before running the script.


export DEBUG=TRUE



The labels are comments, so won't cause syntax errors if disable our goto's (by setting goto to the ':' no-op), but this means we need to quote them in our goto statements.


goto


goto


:


goto



Whenever using any kind of goto solution, you need to be careful that the code you're jumping past doesn't set any variables that you rely on later - you may need to move those definitions to the top of your script, or just above one of your goto statements.


goto


goto





Without getting into the good/bad-ness of goto's, Laurence's solution is just cool. You got my vote.
– Mogens TrasherDK
Dec 4 '17 at 4:27





That's more like a skip-to, an if(false) seems to make more sense (to me).
– vesperto
Feb 23 at 8:56


if(false)



Although others have already clarified that there is no direct goto equivalent in bash (and provided the closest alternatives such as functions, loops, and break), I would like to illustrate how using a loop plus break can simulate a specific type of goto statement.


goto


break



The situation where I find this the most useful is when I need to return to the beginning of a section of code if certain conditions are not met. In the example below, the while loop will run forever until ping stops dropping packets to a test IP.


#!/bin/bash

TestIP="8.8.8.8"

# Loop forever (until break is issued)
while true; do

# Do a simple test for Internet connectivity
PacketLoss=$(ping "$TestIP" -c 2 | grep -Eo "[0-9]+% packet loss" | grep -Eo "^[0-9]")

# Exit the loop if ping is no longer dropping packets
if [ "$PacketLoss" == 0 ]; then
echo "Connection restored"
break
else
echo "No connectivity"
fi
done



There is one more ability to achieve a desired results: command trap. It can be used to clean-up purposes for example.


trap



There is no goto in bash.


goto



Here is some dirty workaround using trap which jumps only backwards:)


trap


#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT

echo foo
goto trap 2> /dev/null
echo bar



Output:


$ ./test.sh
foo
I am
here now.



This shouldn't be used in that way, but only for educational purposes. Here is why this works:



trap is using exception handling to achieve the change in code flow.
In this case the trap is catching anything that causes the script to EXIT. The command goto doesn't exist, and hence throws an error, which would ordinarily exit the script. This error is being caught with trap, and the 2>/dev/null hides the error message that would ordinarily be displayed.


trap


trap


goto


trap


2>/dev/null



This implementation of goto is obviously not reliable, since any non-existent command (or any other error, for that manner), would execute the same trap command. In particular, you cannot choose which label to go-to.



Basically in real scenario you don't need any goto statements, they're redundant as random calls to different places only make your code difficult to understand.



If your code is invoked many times, then consider to use loop and changing its workflow to use continue and break.


continue


break



If your code repeats it-self, consider writing the function and calling it as many times as you want.



If your code needs to jump into specific section based on the variable value, then consider using case statement.


case



If you can separate your long code into smaller pieces, consider moving it into separate files and call them from the parent script.





what's the differences between this form and a normal function?
– yurenchen
Jun 1 '16 at 2:39





@yurenchen - Think of trap as using exception handling to achieve the change in code flow. In this case the trap is catching anything that causes the script to EXIT, which is triggered by invoking the non-existent command goto. BTW: The argument goto trap could be anything, goto ignored because it is the goto that causes the EXIT, and the 2>/dev/null hides the error message saying your script is exiting.
– Jesse Chisholm
Dec 5 '16 at 19:28


trap


exception handling


EXIT


goto


goto trap


goto ignored


goto


2>/dev/null



I found out a way to do this using functions.



Say, for example, you have 3 choices: A, B, and C. A and Bexecute a command, but C gives you more info and takes you to the original prompt again. This can be done using functions.


A


B


C


A


B


C



Note that since the line containg function demoFunction is just setting up the function, you need to call demoFunction after that script so the function will actually run.


function demoFunction


demoFunction



You can easily adapt this by writing multiple other functions and calling them if you need to "GOTO" another place in your shell script.


GOTO


function demoFunction C) printf "ntoption A runs pwd, option B runs lsn" && demoFunction;;
esac


demoFunction



This is a small correction of the Judy Schmidt script put up by Hubbbitus.



Putting non-escaped labels in the script was problematic on the machine and caused it to crash. This was easy enough to resolve by adding # to escape the labels. Thanks to Alexej Magura and access_granted for their suggestions.


#!/bin/bash
# include this boilerplate
function goto
label=$1
cmd=$(sed -n "/$#label#:/:a;n;p;ba;" $0

start=$1:-"start"

goto $start

#start#
echo "start"
goto bing

#boom#
echo boom
goto eof

#bang#
echo bang
goto boom

#bing#
echo bing
goto bang

#eof#
echo "the end mother-hugger..."





This copy paste is not working => there is still a jumpto.
– Alexis Paques
Aug 3 at 11:18





What does that mean? What's not working? Could you be more specific?
– thebunnyrules
Aug 5 at 16:26





Did you try your code?
– Alexis Paques
Aug 5 at 19:37





@thebunnyrules I ran into the same issue as you but solved it differently +1 for your solution though!
– Fabby
Aug 30 at 18:22





@Fabby thanks! I'll have a read through it.
– thebunnyrules
Sep 1 at 2:43



This solution had the following issues:


:


label:



Here's a fixed (shell-check clean) version:


shell-check


#!/bin/bash

# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
function goto

local label=$1
cmd=$(sed -En "/^[[:space:]]*#[[:space:]]*$label:[[:space:]]*#/:a;n;p;ba;" "$0")
eval "$cmd"
exit


start=$1:-start
goto "$start" # GOTO start: by default

#start:# Comments can occur after labels
echo start
goto end

# skip: # Whitespace is allowed
echo this is usually skipped

# end: #
echo end




Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



Would you like to answer one of these unanswered questions instead?

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)