Нет, gotoв Bash нет (по крайней мере, это говорит command not foundдля меня). Зачем? Скорее всего, есть лучший способ сделать это.
Никлас Б.
153
У него могут быть свои причины. Я нашел этот вопрос, потому что хочу, чтобы gotoоператор пропустил много кода для отладки большого скрипта, не дожидаясь часа, пока не будут выполнены различные несвязанные задачи. Я бы, конечно, не использовал a gotoв рабочем коде, но для отладки моего кода это сделало бы мою жизнь бесконечно проще, и было бы легче заметить, когда это удастся удалить.
Карл Николл
30
@delnan Но отсутствие goto может усложнить некоторые вещи. Там действительно есть варианты использования.
glglgl
72
Мне надоел этот миф о гото! С Goto все в порядке! Все, что ты пишешь, в конце концов становится гото В ассемблере есть только goto. Хорошей причиной для использования goto в более высоких языках программирования является, например, чистое и удобочитаемое выпрыгивание из вложенных циклов.
Александр Чутро
25
«Избегай goto» - великое правило. Как и любое правило, его следует изучать в три этапа. Первое: следуйте правилу, пока оно не станет второй натурой. Во-вторых, научиться понимать причины правила. В-третьих, выучите хорошие исключения из правила, основанные на всестороннем понимании того, как следовать правилу и причин этого правила. Старайтесь не пропускать шаги, описанные выше, что было бы похоже на использование «goto». ;-)
Джефф Лирман
Ответы:
75
Нет, нет; см §3.2.4 «Составные команды» в Bash Reference Manual для получения информации о структурах управления , которые делают существование. В частности, обратите внимание на упоминание breakи continue, которые не так гибки, как goto, но более гибки в Bash, чем в некоторых языках, и могут помочь вам достичь того, чего вы хотите. (Что бы вы ни хотели ...)
Не могли бы вы рассказать о «более гибком в Bash, чем в некоторых языках»?
user239558
21
@ user239558: Некоторые языки позволяют вам входить breakили выходить continueиз самого внутреннего цикла, в то время как Bash позволяет вам указать, сколько уровней цикла должно пройти. (И даже в тех языках, которые позволяют вам выполнять произвольные циклы breakили выходить continueиз них, большинство требует, чтобы они были выражены статически, например, break foo;выходили из цикла с меткой, fooтогда как в Bash это выражалось динамически, например, break "$foo"выходили из $fooциклов. )
ruakh
Я бы порекомендовал определять функции с необходимыми функциями и вызывать их из других частей кода.
blmayer
@ruakh На самом деле, многие языки позволяют break LABEL_NAME;, а не отвратительно Баш break INTEGER;.
Sapphire_Brick
1
@Sapphire_Brick: Да, я упоминал об этом в комментарии, на который вы отвечаете.
Руах
125
Если вы используете его, чтобы пропустить часть большого скрипта для отладки (см. Комментарий Карла Николла), то если 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» довольно проста и запоминаема, но как найти подходящий файл? Если ваш редактор позволяет блокировать отступ, вы можете сделать отступ пропущенному блоку (тогда вы захотите вернуть его, когда закончите). Или комментарий к файлу, но это должно быть что-то, что вы запомните, что, я подозреваю, будет зависеть от программиста.
Да falseвсегда доступно. Но если у вас есть блок кода, который вы не хотите выполнять, просто закомментируйте его. Или удалите его (и посмотрите в вашей системе контроля версий, если вам нужно восстановить его позже).
Кит Томпсон
1
Если этот блок кода слишком длинный, чтобы нудно комментировать одну строку за раз, посмотрите эти приемы. stackoverflow.com/questions/947897/… Тем не менее, они не помогают текстовому редактору сопоставить начало и конец, так что они не слишком улучшены.
Камиль Гудесюн
4
«if false» часто намного лучше, чем комментирование кода, потому что это гарантирует, что вложенный код продолжает оставаться юридическим кодом. Лучшее оправдание для комментирования кода - это когда его действительно нужно удалить, но есть что-то, что нужно запомнить - так что это просто комментарий, а не «код».
Джефф Лирман
1
Я использую комментарий «#if false;». Таким образом, я могу просто найти это и найти начало и конец раздела удаления отладки.
Джон
За этот ответ не следует голосовать, поскольку он никак не отвечает на вопрос ФП.
Виктор Жорас
54
Это действительно может быть полезно для некоторых отладочных или демонстрационных нужд.
Обратите внимание, что это требует bash v4.0+. Это, однако, не универсальный, gotoа сквозной вариант для caseзаявления.
mklement0
3
я думаю, что это должен быть ответ. Мне действительно нужно перейти для поддержки возобновления выполнения сценария из данной инструкции. это, во всех отношениях, но семантика, goto, и семантика и синтаксические сахара симпатичны, но не строго необходимы. отличное решение, ИМО.
Натан г
1
@nathang, будь то ответ зависит от того, происходит ли ваш случай к сетке с подмножеством общего случая ОП спросил о. К сожалению, вопрос задает об общем случае, делая этот ответ слишком узким, чтобы быть правильным. (Должен ли этот вопрос быть закрыт как слишком широкий по этой причине - другое обсуждение).
Чарльз Даффи,
1
Goto - это больше, чем выбор. Функция goto означает, что вы можете прыгать в места в соответствии с некоторыми условиями, создавая даже петлю, похожую на поток ...
Серхио Абреу,
19
Если вы тестируете / отлаживаете 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заявлений.
Не вдаваясь в хорошие / плохие идеи Goto, решение Лоуренса просто круто. Вы получили мой голос.
Mogens TrasherDK
1
Это больше похоже на переход, if(false)кажется, имеет больше смысла (для меня).
Весперто
2
Цитирование goto «метка» также означает, что документ here не раскрывается / не оценивается, что избавляет вас от некоторых трудных для поиска ошибок (без кавычек это может быть связано с: расширением параметров, подстановкой команд и арифметическим расширением, которые все могут иметь побочные эффекты). Вы также можете немного подправить это alias goto=":<<"и catвообще отказаться от него .
mrsspuratic
да, Весперто, вы можете использовать оператор «если», и я сделал это во многих сценариях, но он становится очень грязным и его намного сложнее контролировать и отслеживать, особенно если вы хотите часто менять точки перехода.
Лоуренс Реншоу
12
Хотя другие уже пояснили, что gotoв bash нет прямого эквивалента (и предоставили наиболее близкие альтернативы, такие как функции, циклы и разрывы), я хотел бы проиллюстрировать, как использование цикла plus breakможет имитировать определенный тип оператора goto.
Ситуация, когда я нахожу это наиболее полезным, - это когда мне нужно вернуться к началу раздела кода, если определенные условия не выполняются. В приведенном ниже примере цикл while будет работать вечно, пока ping не перестанет сбрасывать пакеты на тестовый IP.
#!/bin/bashTestIP="8.8.8.8"# Loop forever (until break is issued)while true;do# Do a simple test for Internet connectivityPacketLoss=$(ping "$TestIP"-c 2| grep -Eo"[0-9]+% packet loss"| grep -Eo"^[0-9]")# Exit the loop if ping is no longer dropping packetsif["$PacketLoss"==0];then
echo "Connection restored"breakelse
echo "No connectivity"fidone
#!/bin/bash# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461function 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
Вот некоторый грязный обходной путь, 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, очевидно, ненадежна, поскольку любая несуществующая команда (или любая другая ошибка, связанная с этим способом) будет выполнять ту же команду прерывания. В частности, вы не можете выбрать, какой ярлык перейти.
По сути, в реальном сценарии вам не нужны никакие операторы goto, они избыточны, поскольку случайные вызовы в разные места только затрудняют понимание вашего кода.
Если ваш код вызывается много раз, рассмотрите возможность использования цикла и изменения его рабочего процесса для использования continueи break.
Если ваш код повторяет сам, подумайте о написании функции и вызове ее столько раз, сколько вы хотите.
Если ваш код должен перейти в конкретный раздел, основанный на значении переменной, то подумайте об использовании caseоператора.
Если вы можете разделить свой длинный код на более мелкие части, рассмотрите возможность его перемещения в отдельные файлы и вызова их из родительского сценария.
в чем разница между этой формой и нормальной функцией?
yurenchen
2
@yurenchen - Подумайте об trapиспользовании exception handlingдля достижения изменений в потоке кода. В этом случае ловушка перехватывает все, что вызывает сценарий EXIT, который запускается при вызове несуществующей команды goto. Кстати: аргумент goto trapможет быть любым, goto ignoredпотому что именно он gotoвызывает EXIT, и 2>/dev/nullскрывает сообщение об ошибке, сообщающее, что ваш скрипт завершается.
Джесси Чисхолм
2
Это небольшое исправление сценария Джуди Шмидт, созданного Хабббитом.
Помещение в сценарий неэкранированных меток было проблематично на компьютере и приводило к его аварийному завершению. Это было достаточно легко решить, добавив #, чтобы убрать метки. Спасибо Алексею Магуре и access_granted за их предложения.
#!/bin/bash# include this boilerplatefunction 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..."
Что это значит? Что не работает? Не могли бы Вы уточнить?
thebunnyrules
Конечно, я попробовал код! Я проверил в 5 или 6 различных условиях, прежде чем опубликовать все успешно. Как код потерпел неудачу для вас, какое сообщение об ошибке или код?
thebunnyrules
Каким бы ни был мужчина. Я хочу помочь вам, но вы действительно расплывчаты и необщительны (не говоря уже об оскорблении этим вопросом «Вы проверяли это»). Что значит «ты не правильно вставил»? Если вы имеете в виду "/ $ # label #: / {: a; n; p; ba};" часть, я очень хорошо понимаю, что это другое. Я изменил его для нового формата меток (aka # label # vs: label в другом ответе, который в некоторых случаях вызывал сбой скрипта). У вас есть какая-то действительная проблема с моим ответом (например, сбой скрипта или неправильный синтаксис) или вы просто -1 мой ответ, потому что вы подумали: «Я не знаю, как вырезать и вставлять»?
thebunnyrules
1
@thebunnyrules Я столкнулся с той же проблемой, что и вы, но решил по-другому +1, хотя для вашего решения!
Fabby
2
Простой способ поиска с возможностью комментирования блоков кода при отладке.
GOTO=false
if ${GOTO};then
echo "GOTO failed"...fi# End of GOTO
echo "GOTO done"
Скажем, к примеру, у вас есть 3 варианта: A, Bи C. Aи Bвыполните команду, но Cвы получите больше информации и снова перейдете к исходному приглашению. Это можно сделать с помощью функций.
Обратите внимание, что, поскольку линия связи 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
goto
в Bash нет (по крайней мере, это говоритcommand not found
для меня). Зачем? Скорее всего, есть лучший способ сделать это.goto
оператор пропустил много кода для отладки большого скрипта, не дожидаясь часа, пока не будут выполнены различные несвязанные задачи. Я бы, конечно, не использовал agoto
в рабочем коде, но для отладки моего кода это сделало бы мою жизнь бесконечно проще, и было бы легче заметить, когда это удастся удалить.Ответы:
Нет, нет; см §3.2.4 «Составные команды» в Bash Reference Manual для получения информации о структурах управления , которые делают существование. В частности, обратите внимание на упоминание
break
иcontinue
, которые не так гибки, какgoto
, но более гибки в Bash, чем в некоторых языках, и могут помочь вам достичь того, чего вы хотите. (Что бы вы ни хотели ...)источник
break
или выходитьcontinue
из самого внутреннего цикла, в то время как Bash позволяет вам указать, сколько уровней цикла должно пройти. (И даже в тех языках, которые позволяют вам выполнять произвольные циклыbreak
или выходитьcontinue
из них, большинство требует, чтобы они были выражены статически, например,break foo;
выходили из цикла с меткой,foo
тогда как в Bash это выражалось динамически, например,break "$foo"
выходили из$foo
циклов. )break LABEL_NAME;
, а не отвратительно Башbreak INTEGER;
.Если вы используете его, чтобы пропустить часть большого скрипта для отладки (см. Комментарий Карла Николла), то если false может быть хорошим вариантом (не уверен, всегда ли доступен false, для меня он находится в / bin / false) :
Трудность возникает, когда приходит время вырвать ваш код отладки. Конструкция «if false» довольно проста и запоминаема, но как найти подходящий файл? Если ваш редактор позволяет блокировать отступ, вы можете сделать отступ пропущенному блоку (тогда вы захотите вернуть его, когда закончите). Или комментарий к файлу, но это должно быть что-то, что вы запомните, что, я подозреваю, будет зависеть от программиста.
источник
false
всегда доступно. Но если у вас есть блок кода, который вы не хотите выполнять, просто закомментируйте его. Или удалите его (и посмотрите в вашей системе контроля версий, если вам нужно восстановить его позже).Это действительно может быть полезно для некоторых отладочных или демонстрационных нужд.
Я обнаружил, что решение Боба Коупленда http://bobcopeland.com/blog/2012/10/goto-in-bash/ Elegant:
результаты в:
источник
: start:
чтобы они не были синтаксическими ошибками.set -x
помогает понять, что происходитВы можете использовать
case
в bash для имитации goto:производит:
источник
bash v4.0+
. Это, однако, не универсальный,goto
а сквозной вариант дляcase
заявления.Если вы тестируете / отлаживаете bash-скрипт и просто хотите пропустить форварды после одного или нескольких разделов кода, вот очень простой способ сделать это, который также очень легко найти и удалить позже (в отличие от большинства методов описано выше).
Чтобы вернуть скрипт в нормальное состояние, просто удалите все строки с помощью
GOTO
.Мы также можем протестировать это решение, добавив
goto
команду в качестве псевдонима:Псевдонимы обычно не работают в скриптах bash, поэтому нам нужна
shopt
команда, чтобы это исправить.Если вы хотите иметь возможность включать / отключать свои
goto
, нам нужно немного больше:Затем вы можете сделать
export DEBUG=TRUE
перед запуском сценария.Метки являются комментариями, поэтому не будут вызывать синтаксические ошибки, если отключить наши
goto
(установивgoto
«:
no-op»), но это означает, что нам нужно заключить их в кавычкиgoto
утверждениях.Всякий раз, когда вы используете какое-либо
goto
решение, вы должны быть осторожны, чтобы код, через который вы переходите, не устанавливал переменные, на которые вы полагаетесь позже, - вам может потребоваться переместить эти определения в начало вашего сценария или чуть выше одного. вашихgoto
заявлений.источник
if(false)
кажется, имеет больше смысла (для меня).alias goto=":<<"
иcat
вообще отказаться от него .Хотя другие уже пояснили, что
goto
в bash нет прямого эквивалента (и предоставили наиболее близкие альтернативы, такие как функции, циклы и разрывы), я хотел бы проиллюстрировать, как использование цикла plusbreak
может имитировать определенный тип оператора goto.Ситуация, когда я нахожу это наиболее полезным, - это когда мне нужно вернуться к началу раздела кода, если определенные условия не выполняются. В приведенном ниже примере цикл while будет работать вечно, пока ping не перестанет сбрасывать пакеты на тестовый IP.
источник
Это решение имело следующие проблемы:
:
label:
любом месте строки как меткуВот исправленная (
shell-check
чистая) версия:источник
Есть еще одна возможность достичь желаемых результатов: команда
trap
. Например, его можно использовать для очистки.источник
Здесь нет
goto
в bash.Вот некоторый грязный обходной путь,
trap
который прыгает только назад :)Вывод:
Это не должно использоваться таким образом, но только в образовательных целях. Вот почему это работает:
trap
использует обработку исключений для достижения изменения в потоке кода. В этом случаеtrap
перехватывает все, что заставляет скрипт завершиться. Командаgoto
не существует, и, следовательно, выдает ошибку, которая обычно завершает работу сценария. Эта ошибка обнаруживаетсяtrap
, и2>/dev/null
скрывает сообщение об ошибке, которое обычно отображается.Эта реализация goto, очевидно, ненадежна, поскольку любая несуществующая команда (или любая другая ошибка, связанная с этим способом) будет выполнять ту же команду прерывания. В частности, вы не можете выбрать, какой ярлык перейти.
По сути, в реальном сценарии вам не нужны никакие операторы goto, они избыточны, поскольку случайные вызовы в разные места только затрудняют понимание вашего кода.
Если ваш код вызывается много раз, рассмотрите возможность использования цикла и изменения его рабочего процесса для использования
continue
иbreak
.Если ваш код повторяет сам, подумайте о написании функции и вызове ее столько раз, сколько вы хотите.
Если ваш код должен перейти в конкретный раздел, основанный на значении переменной, то подумайте об использовании
case
оператора.Если вы можете разделить свой длинный код на более мелкие части, рассмотрите возможность его перемещения в отдельные файлы и вызова их из родительского сценария.
источник
trap
использованииexception handling
для достижения изменений в потоке кода. В этом случае ловушка перехватывает все, что вызывает сценарийEXIT
, который запускается при вызове несуществующей командыgoto
. Кстати: аргументgoto trap
может быть любым,goto ignored
потому что именно онgoto
вызывает EXIT, и2>/dev/null
скрывает сообщение об ошибке, сообщающее, что ваш скрипт завершается.Это небольшое исправление сценария Джуди Шмидт, созданного Хабббитом.
Помещение в сценарий неэкранированных меток было проблематично на компьютере и приводило к его аварийному завершению. Это было достаточно легко решить, добавив #, чтобы убрать метки. Спасибо Алексею Магуре и access_granted за их предложения.
источник
Простой способ поиска с возможностью комментирования блоков кода при отладке.
Результат-> GOTO сделано
источник
Я нашел способ сделать это с помощью функций.
Скажем, к примеру, у вас есть 3 варианта:
A
,B
иC
.A
иB
выполните команду, ноC
вы получите больше информации и снова перейдете к исходному приглашению. Это можно сделать с помощью функций.Обратите внимание, что, поскольку линия связи
function demoFunction
просто устанавливает функцию, вам нужно вызватьdemoFunction
после этого сценария, чтобы функция фактически выполнялась.Вы можете легко адаптировать это, написав несколько других функций и вызывая их, если вам нужно «
GOTO
» другое место в вашем скрипте оболочки.источник