Есть ли заявление "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..."