Есть ли заявление "goto" в bash?
есть ли заявление "Гото" в bash ? Я знаю, что это считается плохой практикой, но мне нужно конкретно "Гото".
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 (и при условии ближайших альтернатив, таких как функции, циклы и разрыв), я хотел бы проиллюстрировать, как использовать цикл plusbreakможет имитировать определенный тип оператора 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..."