bash中是否有“goto”声明?
bash中是否有“goto”声明? 我知道这被认为是不好的做法,但我特别需要“转到”。
不,那里没有; 请参阅Bash参考手册中的第3.2.4节“复合命令”了解有关存在的控制结构的信息。 尤其要注意提及break
和continue
,它不像goto
那样灵活,但在Bash中比在某些语言中更灵活,并且可以帮助你实现你想要的。 (无论你想要什么…)
如果你正在使用它跳过一个大脚本的一部分进行debugging(见Karl Nicoll的评论),那么如果false是一个好的select(不知道“false”总是可用的,对我来说它是在/ bin / false) :
# ... Code I want to run here ... if false; then # ... Code I want to skip here ... fi # ... I want to resume here ...
当你需要翻译你的debugging代码的时候,就会遇到困难。 “如果错误”构造是相当直接和令人难忘的,但你如何find匹配的fi? 如果您的编辑器允许您阻止缩进,则可以缩进跳过的块(然后在完成时您将要放回原位)。 或者在网上发表评论,但这将是你会记得的事情,我怀疑它会依赖于程序员。
对于某些debugging或演示需求确实可能是有用的。
我发现,朱迪·施密特仿真http://bobcopeland.com/blog/2012/10/goto-in-bash/ helpfull:
#!/bin/bash # include this boilerplate function jumpto { label=$1 cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | 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
结果是:
$ ./test.sh x is 100 $ ./test.sh foo x is 10 $ ./test.sh mid This is not printed! x is 101
你可以在bash中使用case
来模拟goto:
#!/bin/bash case bar in foo) echo foo ;& bar) echo bar ;& *) echo star ;; esac
生产:
bar star
虽然其他人已经阐明了在bash中没有直接的goto
等价(并提供了函数,循环和break等最接近的替代方法),但我想说明如何使用循环加break
来模拟特定types的goto语句。
我发现这种情况最有用的情况是,如果某些条件没有得到满足,我需要返回到一段代码的开头。 在下面的例子中,while循环将永远运行,直到ping停止丢弃数据包到testingIP。
#!/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
还有一个能够达到预期结果的能力:命令trap
。 例如,它可以用于清理目的。
在bash中没有goto
。
这是一些使用trap
只是向后跳转的肮脏的解决方法:)
#!/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
是使用exception处理来实现代码stream的变化。 在这种情况下, trap
捕捉任何导致脚本退出的东西。 goto
命令不存在,因此抛出一个错误,通常会退出脚本。 这个错误被trap
, 2>/dev/null
隐藏了通常会显示的错误信息。
goto的这种实现显然是不可靠的,因为任何不存在的命令(或者任何其他的错误)都会执行相同的trap命令。 特别是,你不能select去哪个标签。
基本上在实际情况下,你不需要任何goto语句,它们是多余的,因为随机调用不同的地方只会让你的代码难以理解。
如果您的代码被多次调用,则考虑使用循环并更改其工作stream程以使用continue
和break
。
如果您的代码重复它自己,请考虑编写函数并根据需要多次调用它。
如果你的代码需要跳转到基于variables值的特定部分,那么考虑使用case
语句。
如果您可以将长代码分成较小的代码,请考虑将其移动到单独的文件中,并从父脚本中调用它们。
如果您正在testing/debugging一个bash脚本,并且只想跳过一个或多个代码段,那么这是一个非常简单的方法,以后也很容易find和删除(不像大多数的方法如上所述)。
#!/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
命令作为别名来美化这个解决scheme:
#!/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
语句中引用它们。
每当使用任何types的goto
解决scheme时,都需要注意,您跳过的代码不会设置任何后来依赖的variables – 您可能需要将这些定义移动到脚本的顶部,你的goto
语句之一。
我find了一种使用函数来做到这一点的方法。
比如说,你有三种select: A
, B
和C
A
和B
执行一个命令,但是C
给了你更多的信息,并把你带到原来的提示。 这可以使用函数来完成。
请注意,由于行包含function demoFunction
只是设置函数,因此您需要在该脚本之后调用demoFunction
,以便函数实际运行。
如果需要在shell脚本中“ GOTO
”另一个地方,可以通过编写多个其他函数并调用它们来轻松地进行调整。
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