Linux bash 中是否有“goto”语句?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9639103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a "goto" statement in bash?
提问by kofucii
Is there a "goto" statement in bash ? I know It is considered bad practice, but I need specifically "goto".
bash 中是否有“goto”语句?我知道这被认为是不好的做法,但我特别需要“转到”。
采纳答案by ruakh
No, there is not; see §3.2.4 "Compound Commands" in the Bash Reference Manualfor information about the control structures that doexist. 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 . . .)
不,那里没有; 有关确实存在的控制结构的信息,请参阅Bash 参考手册中的第 3.2.4 节“复合命令”。特别要注意的提和,这是不够灵活,但更灵活的Bash比某些语言,并且可以帮助你实现你想要的。(无论你想要什么......)break
continue
goto
回答by Paul Brannan
You can use case
in bash to simulate a goto:
您可以case
在 bash中使用来模拟 goto:
#!/bin/bash
case bar in
foo)
echo foo
;&
bar)
echo bar
;&
*)
echo star
;;
esac
produces:
产生:
bar
star
回答by Michael Rusch
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):
如果您使用它来跳过用于调试的大型脚本的一部分(请参阅 Karl Nicoll 的评论),那么如果 false 可能是一个不错的选择(不确定“false”是否始终可用,对我来说它在 /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.
当需要删除调试代码时,困难就来了。“if false”结构非常简单且令人难忘,但是您如何找到匹配的 fi 呢?如果你的编辑器允许你阻止缩进,你可以缩进跳过的块(然后你会想在你完成后把它放回去)。或者对 fi 行的评论,但它必须是你会记住的,我怀疑这将非常依赖于程序员。
回答by Serge Roussak
There is one more ability to achieve a desired results: command trap
. It can be used to clean-up purposes for example.
还有一种实现预期结果的能力: command trap
。例如,它可以用于清理目的。
回答by Hubbitus
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:
我发现 Bob Copeland 解决方案http://bobcopeland.com/blog/2012/10/goto-in-bash/优雅:
#!/bin/bash
# include this boilerplate
function jumpto
{
label=
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101
| 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:
结果是:
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
回答by kenorb
There is no goto
in bash.
goto
bash 中没有。
Here is some dirty workaround using trap
which jumps only backwards:)
这是一些肮脏的解决方法,使用trap
它只向后跳转:)
$ ./test.sh
foo
I am
here now.
Output:
输出:
function demoFunction {
read -n1 -p "Pick a letter to run a command [A, B, or C for more info] " runCommand
case $runCommand in
a|A) printf "\n\tpwd being executed...\n" && pwd;;
b|B) printf "\n\tls being executed...\n" && ls;;
c|C) printf "\n\toption A runs pwd, option B runs ls\n" && demoFunction;;
esac
}
demoFunction
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.
goto 的这种实现显然是不可靠的,因为任何不存在的命令(或任何其他错误,对于这种方式),都会执行相同的陷阱命令。特别是,您无法选择要转到的标签。
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.
基本上在实际场景中您不需要任何 goto 语句,它们是多余的,因为对不同位置的随机调用只会使您的代码难以理解。
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.
如果您可以将长代码分成更小的部分,请考虑将其移动到单独的文件中并从父脚本中调用它们。
回答by cutrightjm
I found out a way to do this using functions.
我找到了一种使用函数来做到这一点的方法。
Say, for example, you have 3 choices: A
, B
, and C
. A
and B
execute a command, but C
gives you more info and takes you to the original prompt again. This can be done using functions.
说,例如,你有3种选择: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
”shell 脚本中的另一个位置时调用它们来轻松地适应这一点。
#!/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
回答by Seth McCauley
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
bash 中没有直接的等价物(并提供了最接近的替代方案,例如函数、循环和中断),但我想说明如何使用循环加号break
来模拟特定类型的 goto 语句。
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.
我发现这最有用的情况是,如果不满足某些条件,我需要返回到一段代码的开头。在下面的示例中,while 循环将永远运行,直到 ping 停止将数据包丢弃到测试 IP。
#!/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"
回答by Laurence Renshaw
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).
如果您正在测试/调试 bash 脚本,并且只想跳过一段或多段代码,这里有一种非常简单的方法可以做到,以后也很容易找到和删除(与大多数方法不同)如上所述)。
#!/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"
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
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"
Aliases don't usually work in bash scripts, so we need the shopt
command to fix that.
别名通常在 bash 脚本中不起作用,所以我们需要shopt
命令来解决这个问题。
If you want to be able to enable/disable your goto
's, we need a little bit more:
如果您希望能够启用/禁用您的goto
's,我们需要多一点:
#!/bin/bash
# include this boilerplate
function goto {
label=
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" ##代码## | grep -v ':$')
eval "$cmd"
exit
}
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..."
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
's(通过设置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
陈述。
回答by thebunnyrules
This is a small correction of the Judy Schmidt script put up by Hubbbitus.
这是对 Hubbbitus 提出的 Judy Schmidt 剧本的一个小修正。
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.
在脚本中放置非转义标签在机器上是有问题的,并导致它崩溃。通过添加 # 来转义标签,这很容易解决。感谢 Alexej Magura 和 access_granted 的建议。
##代码##