Есть ли заявление "goto" в bash?


есть ли заявление "Гото" в bash ? Я знаю, что это считается плохой практикой, но мне нужно конкретно "Гото".

10 156

10 ответов:

нет, нет; см. §3.2.4 "составные команды" в Справочное Руководство Bash для получения информации о структурах управления, что do. В частности, обратите внимание на упоминание break и continue, которые не так гибки, как goto, но более гибки в Bash, чем в некоторых языках, и может помочь вам достичь того, что вы хотите. (Все, что вы хотите . . .)

Если вы используете его, чтобы пропустить часть большого скрипта для отладки (см. комментарий Карла Николла), то если 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, но это должно быть что-то, что вы запомните, что, я подозреваю, будет очень зависимым от программиста.

Это действительно может быть полезно для некоторых отладки или демонстрации должен.

Я нашел это решение Боба Коупленда 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};"  | 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

можно использовать case в Баш, чтобы имитировать Гото:

#!/bin/bash

case bar in
  foo)
    echo foo
    ;&

  bar)
    echo bar
    ;&

  *)
    echo star
    ;;
esac

выдает:

bar
star

если вы тестируете / отлаживаете сценарий 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.

мы также можем украсить это решение, добавив в качестве псевдонима:

#!/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 ' s, нам нужно немного больше:

#!/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's (по параметр goto до: ' no-op), но это означает, что мы должны цитировать их в нашем goto заявления.

при использовании любого вида goto решение, вам нужно быть осторожным, чтобы код, который вы прыгаете мимо, не устанавливал никаких переменных, на которые вы полагаетесь позже - вам может потребоваться переместить эти определения в верхнюю часть вашего скрипта или чуть выше одного из ваших goto заявления.

хотя другие уже уточнили, что прямого goto эквивалент в bash (и при условии ближайших альтернатив, таких как функции, циклы и разрыв), я хотел бы проиллюстрировать, как использовать цикл plus break может имитировать определенный тип оператора goto.

ситуация, когда я нахожу это наиболее полезным, когда мне нужно вернуться к началу раздела кода, если определенные условия не выполняются. В приведенном ниже примере будет выполняться цикл while навсегда, пока ping не прекратит отбрасывать пакеты на тестовый IP.

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

есть еще одна возможность добиться желаемого результата: command trap. Он может быть использован для очистки для примера.

нет goto в bash.

вот некоторые грязные обходные пути с помощью 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, они избыточны, поскольку случайные вызовы в разные места только затрудняют понимание вашего кода.

если ваш код вызывается много раз, то рассмотрите возможность использования цикла и изменения его рабочего процесса для использования continue и break.

если ваш код повторяется, подумайте о том, чтобы написать функцию и вызвать ее столько раз, сколько вы хотите.

если ваш код должен перейти в конкретный раздел, на основе переменной значение, а затем рассмотреть возможность использования case заявление.

если вы можете разделить свой длинный код на более мелкие части, подумайте о том, чтобы переместить его в отдельные файлы и вызвать их из родительского скрипта.

я нашел способ сделать это с помощью функции.

скажем, например, у вас есть 3 варианта: A,B и C. A и Bвыполнить команду, но C дает вам больше информации и снова приведет вас к исходному приглашению. Это можно сделать с помощью функции.

обратите внимание, что с линии containg function demoFunction просто настройка функции, вам нужно вызвать demoFunction после этого скрипта, так что функция будет работать на самом деле.

вы можно легко адаптировать это, написав несколько других функций и вызывая их, если вам нужно "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

Это небольшая коррекция сценария Джуди Шмидт, поставленного Хаббитусом.

класть не сбежал метки в скрипте было проблематично на машину и стало причиной аварии. Это было достаточно легко решить, добавив #, чтобы избежать ярлыков. Спасибо Алексею Магуре и access_granted за их предложения.

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