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


当前回答

如果你使用它来跳过一个大型脚本的一部分进行调试(参见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线上的注释,但它必须是你能记住的东西,我怀疑这是非常依赖于程序员的。

其他回答

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

我发现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

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

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

结果是-> GOTO done

不,没有;关于存在的控制结构的信息,请参见Bash参考手册中的§3.2.4“复合命令”。特别要注意的是,Bash中提到了break和continue,它们不像goto那样灵活,但在Bash中比在某些语言中更灵活,可能会帮助您实现想要的结果。(无论你想要什么……)

如果您正在测试/调试一个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语句之上。

该解决方案存在以下问题:

不加区别地删除所有以a结尾的代码行: 将标签:一行中的任何位置视为标签

这是一个固定的(shell-check - clean和POSIX兼容)版本:


#!/bin/sh

# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
goto() {
  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