bash中是否有“goto”语句?我知道这被认为是不好的做法,但我需要特别“去”。
当前回答
bash中没有goto。
这里有一些肮脏的工作方法,使用陷阱只向后跳转:)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
输出:
$ ./test.sh
foo
I am
here now.
不应该以这种方式使用,而只能用于教育目的。以下是这种方法有效的原因:
Trap是使用异常处理来实现代码流中的更改。 在本例中,该陷阱将捕获导致脚本退出的任何内容。goto命令不存在,因此会抛出一个错误,该错误通常会退出脚本。此错误被trap捕获,2>/dev/null隐藏了通常显示的错误消息。
goto的这种实现显然是不可靠的,因为任何不存在的命令(或任何其他错误)都会执行相同的trap命令。特别是,您无法选择要使用哪个标签。
基本上在实际场景中,你不需要任何goto语句,它们是多余的,因为随机调用不同的地方只会让你的代码难以理解。
如果您的代码被多次调用,那么考虑使用loop并将其工作流更改为使用continue和break。
如果您的代码重复,请考虑编写函数并尽可能多次地调用它。
如果您的代码需要根据变量值跳转到特定的部分,那么可以考虑使用case语句。
如果可以将长代码分割成较小的片段,请考虑将其移动到单独的文件中,并从父脚本调用它们。
其他回答
bash中没有goto。
这里有一些肮脏的工作方法,使用陷阱只向后跳转:)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
输出:
$ ./test.sh
foo
I am
here now.
不应该以这种方式使用,而只能用于教育目的。以下是这种方法有效的原因:
Trap是使用异常处理来实现代码流中的更改。 在本例中,该陷阱将捕获导致脚本退出的任何内容。goto命令不存在,因此会抛出一个错误,该错误通常会退出脚本。此错误被trap捕获,2>/dev/null隐藏了通常显示的错误消息。
goto的这种实现显然是不可靠的,因为任何不存在的命令(或任何其他错误)都会执行相同的trap命令。特别是,您无法选择要使用哪个标签。
基本上在实际场景中,你不需要任何goto语句,它们是多余的,因为随机调用不同的地方只会让你的代码难以理解。
如果您的代码被多次调用,那么考虑使用loop并将其工作流更改为使用continue和break。
如果您的代码重复,请考虑编写函数并尽可能多次地调用它。
如果您的代码需要根据变量值跳转到特定的部分,那么可以考虑使用case语句。
如果可以将长代码分割成较小的片段,请考虑将其移动到单独的文件中,并从父脚本调用它们。
我找到了一种使用函数的方法。
例如,您有3个选择:A、B和C。A和Bexecute一个命令,但是C给您更多信息,并再次将您带到原始提示符。这可以使用函数来完成。
请注意,由于包含demoFunction函数的那一行只是设置了函数,因此需要在脚本之后调用demoFunction,以便函数能够实际运行。
您可以通过编写多个其他函数并在需要“GOTO”shell脚本中的其他位置时调用它们来轻松地适应这一点。
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
如果你使用它来跳过一个大型脚本的一部分进行调试(参见Karl Nicoll的评论),那么If 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 ...
当需要提取调试代码时,困难就出现了。“if false”结构是相当直接和容易记住的,但你如何找到匹配的fi?如果您的编辑器允许您阻止缩进,那么您可以缩进被跳过的块(然后当您完成时,您将希望将其放回)。或者fi线上的注释,但它必须是你能记住的东西,我怀疑这是非常依赖于程序员的。
如果您正在测试/调试一个bash脚本,并且只想跳过一个或多个代码部分,那么这里有一种非常简单的方法,以后也很容易找到并删除(与上面描述的大多数方法不同)。
#!/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"
要使脚本恢复正常,只需删除GOTO中的任何行。
我们还可以通过添加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"
别名在bash脚本中通常不起作用,因此我们需要使用shopt命令来解决这个问题。
如果你想启用/禁用你的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"
然后在运行脚本之前执行export DEBUG=TRUE。
标签是注释,因此如果禁用goto(通过将goto设置为':' no-op)不会导致语法错误,但这意味着我们需要在goto语句中引用它们。
无论何时使用任何类型的goto解决方案,你都需要小心,你跳过的代码不会设置你以后依赖的任何变量——你可能需要将这些定义移动到脚本的顶部,或者只是在你的goto语句之上。
一个简单的可搜索的goto,用于在调试时注释掉代码块。
GOTO=false
if ${GOTO}; then
echo "GOTO failed"
...
fi # End of GOTO
echo "GOTO done"
结果是-> GOTO done