在bash中有“goto”语句吗?

时间:2012-03-09 18:26:48

标签: linux bash shell goto

bash中有“goto”语句吗?我知道这被认为是不好的做法,但我需要特别“goto”。

12 个答案:

答案 0 :(得分:104)

如果您使用它来跳过部分大型脚本进行调试(请参阅Karl Nicoll的评论),那么如果false可能是一个不错的选择(不确定“false”是否始终可用,对我来说它位于/ bin中/假):

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

if false; then

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

fi

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

当需要删除调试代码时,会遇到困难。 “if false”构造非常简单且令人难忘,但是如何找到匹配的?如果您的编辑器允许您阻止缩进,您可以缩进跳过的块(然后您将在完成后将其放回)。或者对fi行的评论,但它必须是你会记住的东西,我怀疑它将非常依赖程序员。

答案 1 :(得分:65)

不,没有;有关存在的控制结构的信息,请参阅§3.2.4 "Compound Commands" in the Bash Reference Manual。特别要注意提及breakcontinue,它们不像goto那样灵活,但在Bash中比在某些语言中更灵活,可能会帮助您实现目标想。 (无论你想要什么......)

答案 2 :(得分:39)

它确实可能对某些调试或演示需求有用。

我发现Bob Copeland解决方案http://bobcopeland.com/blog/2012/10/goto-in-bash/优雅:

#!/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

答案 3 :(得分:26)

您可以在bash中使用case来模拟转到:

#!/bin/bash

case bar in
  foo)
    echo foo
    ;&

  bar)
    echo bar
    ;&

  *)
    echo star
    ;;
esac

产生

bar
star

答案 4 :(得分:15)

如果您正在测试/调试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设置为':'无操作),则不会导致语法错误,但这意味着我们需要在goto语句中引用它们。

每当使用任何类型的goto解决方案时,您都需要注意,您跳过的代码不会设置您以后依赖的任何变量 - 您可能需要将这些定义移到顶部您的脚本,或者只是在goto个语句之上。

答案 5 :(得分:11)

虽然其他人已经澄清说bash中没有直接的html等价物(并且提供了最接近的选项,如函数,循环和中断),但我想说明如何使用循环加{{1可以模拟特定类型的goto语句。

我认为最有用的情况是,如果不满足某些条件,我需要返回代码段的开头。在下面的示例中,while循环将一直运行,直到ping停止将数据包丢弃到测试IP。

goto

答案 6 :(得分:6)

还有一种能够达到预期效果的能力:命令trap。例如,它可用于清理目的。

答案 7 :(得分:4)

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正在使用异常处理来实现代码流的更改。 在这种情况下,trap正在捕获导致脚本退出的任何内容。命令goto不存在,因此会抛出错误,通常会退出脚本。 trap正在捕获此错误,并且2>/dev/null会隐藏通常会显示的错误消息。

goto的这种实现显然不可靠,因为任何不存在的命令(或任何其他错误,以这种方式)都将执行相同的trap命令。特别是,您无法选择要去的标签。

基本上在实际场景中你不需要任何goto语句,它们是多余的,因为随机调用不同的地方只会让你的代码难以理解。

如果您的代码被多次调用,请考虑使用循环并更改其工作流程以使用continuebreak

如果您的代码重复自我,请考虑编写该函数并根据需要多次调用它。

如果您的代码需要根据变量值跳转到特定部分,请考虑使用case语句。

如果您可以将长代码分成更小的代码,请考虑将其移到单独的文件中并从父代脚本中调用它们。

答案 8 :(得分:4)

This solution出现以下问题:

  • 不加区别地删除所有以:结尾的代码行
  • label:处理为标签上一行的任何地方

这是固定(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

答案 9 :(得分:2)

一个简单的可搜索goto,用于在调试时注释掉代码块。

GOTO=false
if ${GOTO}; then
    echo "GOTO failed"
    ...
fi # End of GOTO
echo "GOTO done"

结果是-> GOTO完成

答案 10 :(得分:1)

我找到了一种使用函数的方法。

比如说,您有3个选择:ABCAB执行命令,但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

答案 11 :(得分:1)

这是对Hubbbitus提出的Judy Schmidt剧本的一个小修正。

在脚本中放置非转义标签在计算机上存在问题并导致其崩溃。通过添加#来转义标签,这很容易解决。感谢Alexej Magura和access_granted的建议。

#!/bin/bash
# include this boilerplate
function goto {  
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | 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..."