Não, não há gotono bash (pelo menos diz command not foundpara mim). Por quê? Provavelmente, existe uma maneira melhor de fazê-lo.
Niklas B.
153
Ele pode ter suas razões. Encontrei essa pergunta porque quero que uma gotoinstrução pule muito código para depurar um script grande sem esperar uma hora para que várias tarefas não relacionadas sejam concluídas. Eu certamente não usaria um gotono código de produção, mas, para depurar meu código, tornaria minha vida infinitamente mais fácil, e seria mais fácil identificar quando se tratava de removê-lo.
10136 Karl-Nicoll
30
@ delnan Mas não ter que ir pode tornar algumas coisas mais complicadas. De fato, existem casos de uso.
glglgl
71
Estou farto desse mito goto! Não há nada errado em ir! Tudo o que você escreve acaba se tornando inútil. No assembler, existe apenas goto. Uma boa razão para usar goto em linguagens de programação mais altas é, por exemplo, saltar de loops aninhados de maneira limpa e legível.
Alexander Czutro
25
"Evite ir" é uma ótima regra. Como qualquer regra, deve ser aprendida em três fases. Primeiro: siga a regra até sua segunda natureza. Segundo, aprenda a entender os motivos da regra. Terceiro, aprenda boas exceções à regra, com base em um entendimento abrangente de como seguir a regra e os motivos da regra. Evite pular as etapas acima, o que seria como usar "goto". ;-)
Jeff Learman
Respostas:
75
Não, não há; ver §3.2.4 "Comandos compostos" na Bash Manual de Referência para obter informações sobre as estruturas de controle que fazer existir. Em particular, observe a menção de breake continue, que não é tão flexível quanto goto, mas é mais flexível no Bash do que em alguns idiomas, e pode ajudá-lo a alcançar o que deseja. (O que você quiser.)
Você poderia expandir para "mais flexível no Bash do que em alguns idiomas"?
user239558
21
@ user239558: Alguns idiomas permitem apenas o loop interno breakou a continuepartir do loop interno, enquanto o Bash permite especificar quantos níveis de loop serão saltos. (E mesmo em idiomas que permitem breakou continuede loops arbitrários, a maioria exige que seja expresso estaticamente - por exemplo, break foo;sairá do loop rotulado foo- enquanto no Bash é expresso dinamicamente - por exemplo, break "$foo"sairá de $fooloops. )
ruach
Eu recomendo definir funções com os recursos necessários e chamá-las de outras partes do código.
blmayer
@ruakh Na verdade, muitos idiomas permitem break LABEL_NAME;, em vez do nojento de Bash break INTEGER;.
Sapphire_Brick
1
@Sapphire_Brick: Sim, eu mencionei isso no comentário que você está respondendo.
ruakh 29/06
124
Se você o estiver usando para pular parte de um script grande para depuração (consulte o comentário de Karl Nicoll), se false puder ser uma boa opção (não tenho certeza se "false" está sempre disponível, para mim está em / bin / false) :
# ... Code I want to run here ...if false;then# ... Code I want to skip here ...fi# ... I want to resume here ...
A dificuldade surge quando é hora de extrair seu código de depuração. A construção "se falsa" é bem direta e memorável, mas como você encontra o fi correspondente? Se o seu editor permitir bloquear o recuo, você poderá recuar o bloco ignorado (será necessário recuperá-lo quando terminar). Ou um comentário sobre a linha fi, mas teria que ser algo que você se lembraria, que eu suspeito que seja muito dependente do programador.
Sim falseestá sempre disponível. Mas se você tem um bloco de código que não deseja executar, basta comentar. Ou exclua-o (e procure no seu sistema de controle de origem se precisar recuperá-lo mais tarde).
perfil completo de Keith Thompson
1
Se o bloco de código for muito longo para comentar tediosamente uma linha de cada vez, veja esses truques. stackoverflow.com/questions/947897/… No entanto, isso também não ajuda um editor de texto a corresponder do início ao fim, portanto, não é uma grande melhoria.
Camille Goudeseune
4
"se falso" geralmente é muito melhor do que comentar o código, porque garante que o código incluído continue sendo o código legal. A melhor desculpa para comentar o código é quando ele realmente precisa ser excluído, mas há algo que precisa ser lembrado - portanto, é apenas um comentário e não mais "código".
Jeff Learman
1
Eu uso o comentário '#if false;'. Dessa forma, eu posso apenas procurar por isso e encontrar o início e o final da seção de remoção de depuração.
Jon
Esta resposta não deve ser votada, pois não responde à pergunta do OP de forma alguma.
21719 Viktor Joras
54
Na verdade, pode ser útil para algumas necessidades de depuração ou demonstração.
Observe que isso requer bash v4.0+. No entanto, não é uma opção de uso geral, gotomas uma opção indireta para a casedeclaração.
usar o seguinte código
3
Eu acho que essa deve ser a resposta. Eu tenho uma necessidade genuína de ir para, a fim de oferecer suporte à retomada da execução de um script, a partir de uma determinada instrução. isto é, em todos os aspectos, exceto açúcares semânticos, goto e semânticos e sintáticos são engraçados, mas não estritamente necessários. ótima solução, IMO.
Nathan g 23/03
1
@nathang, se a resposta depende de seu caso coincidir com o subconjunto do caso geral sobre o qual o OP perguntou. Infelizmente, a pergunta é feita sobre o caso geral, tornando essa resposta muito estreita para ser correta. (Se essa pergunta deve ser encerrada como muito ampla por esse motivo é uma discussão diferente).
Charles Duffy
1
Ir é mais do que selecionar. meios de recurso Goto para ser capaz de saltar para lugares monitores segundo a algumas condições, criando ainda um loop como fluxo ...
Sergio Abreu
19
Se você estiver testando / depurando um script bash e simplesmente quiser pular adiante uma ou mais seções de código, aqui está uma maneira muito simples de fazer isso, que também é muito fácil de encontrar e remover mais tarde (diferente da maioria dos métodos descrito acima).
#!/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"
Para colocar seu script de volta ao normal, exclua todas as linhas com GOTO.
Também podemos prettificar esta solução, adicionando um gotocomando como um alias:
#!/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"
Os aliases geralmente não funcionam em scripts bash, por isso precisamos do shoptcomando para corrigir isso.
Se você deseja ativar / desativar os seus goto, precisamos de um pouco mais:
#!/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"
Então você pode fazer export DEBUG=TRUEantes de executar o script.
Os rótulos são comentários, portanto, não causam erros de sintaxe se desativarmos os nossos goto(definindo gotocomo ' :' não-op ' '), mas isso significa que precisamos citá-los em nossogoto declarações.
Sempre que usar qualquer tipo de gotosolução, você deve tomar cuidado para que o código que você está passando não defina nenhuma variável em que você confia mais tarde - talvez seja necessário mover essas definições para a parte superior do seu script, ou logo acima de uma de suas gotodeclarações.
Sem entrar no bom / mau do goto, a solução de Laurence é simplesmente legal. Você conseguiu meu voto.
Mogens TrasherDK
1
É mais como um pular para, e if(false)parece fazer mais sentido (para mim).
Vesperto
2
Citar o rótulo "goto" também significa que o documento aqui não é expandido / avaliado, o que evita alguns erros difíceis de encontrar (sem aspas, ele estaria sujeito a: expansão de parâmetro, substituição de comando e expansão aritmética, que todos podem ter efeitos colaterais). Você também pode ajustar isso um pouco alias goto=":<<"e dispensar catcompletamente.
precisa saber é o seguinte
sim, vesperto, você pode usar uma declaração 'if', e eu fiz isso em muitos scripts, mas fica muito confuso e é muito mais difícil de controlar e acompanhar, especialmente se você deseja alterar seus pontos de salto com frequência.
Laurence Renshaw
12
Embora outros já tenham esclarecido que não há gotoequivalente direto no bash (e tenham fornecido as alternativas mais próximas, como funções, loops e interrupção), gostaria de ilustrar como o uso de um loop plus breakpode simular um tipo específico de instrução goto.
A situação em que acho isso mais útil é quando preciso retornar ao início de uma seção de código se determinadas condições não forem atendidas. No exemplo abaixo, o loop while será executado para sempre até o ping parar de soltar pacotes para um IP de teste.
#!/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
Aqui está uma solução alternativa suja usando trapqual pula apenas para trás :)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2>/dev/null
echo bar
Resultado:
$ ./test.sh
foo
I am
here now.
Isso não deve ser usado dessa maneira, mas apenas para fins educacionais. Aqui está o porquê disso funcionar:
trapestá usando o tratamento de exceções para obter a alteração no fluxo de código. Nesse caso, trapestá capturando qualquer coisa que faça com que o script EXIT. O comando gotonão existe e, portanto, gera um erro, que normalmente sai do script. Este erro está sendo detectado trape 2>/dev/nulloculta a mensagem de erro que normalmente seria exibida.
Obviamente, essa implementação do goto não é confiável, pois qualquer comando inexistente (ou qualquer outro erro, dessa maneira) executaria o mesmo comando trap. Em particular, você não pode escolher para qual rótulo ir.
Basicamente, no cenário real, você não precisa de nenhuma instrução goto, elas são redundantes, pois chamadas aleatórias para lugares diferentes dificultam a compreensão do seu código.
Se seu código for chamado várias vezes, considere usar loop e altere seu fluxo de trabalho para usar continuee break.
Se o seu código se repetir, considere escrever a função e chamá-la quantas vezes quiser.
Se seu código precisar pular para uma seção específica com base no valor da variável, considere usar a caseinstrução
Se você pode separar seu código longo em partes menores, considere movê-lo para arquivos separados e chame-os a partir do script pai.
Quais são as diferenças entre este formulário e uma função normal?
yurenchen
2
@ yurenchen - Pense trapem usar exception handlingpara conseguir a mudança no fluxo de código. Nesse caso, a armadilha está capturando tudo o que faz com que o script EXITseja acionado, invocando o comando inexistente goto. BTW: O argumento goto trappode ser qualquer coisa, goto ignoredporque é o gotoque causa a EXIT e 2>/dev/nulloculta a mensagem de erro informando que seu script está saindo.
Jesse Chisholm
2
Esta é uma pequena correção do script de Judy Schmidt apresentado por Hubbbitus.
Colocar rótulos não escapados no script foi problemático na máquina e causou uma falha. Isso foi fácil de resolver, adicionando # para escapar dos rótulos. Obrigado a Alexej Magura e access_granted por suas sugestões.
#!/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..."
Esta pasta de cópia não está funcionando => ainda há um jumpto.
Alexis Paques
O que isso significa? O que não está funcionando? Você poderia ser mais específico?
thebunnyrules
Claro que tentei o código! Eu testei em 5 ou 6 condições diferentes antes de postar tudo bem-sucedido. Como o código falhou, qual mensagem ou código de erro?
precisa saber é o seguinte
Qualquer homem. Quero ajudá-lo, mas você está sendo muito vago e pouco comunicativo (para não mencionar o insulto à pergunta "você testou")? O que significa "você não colou corretamente"? Se você estiver se referindo ao "/ $ # label #: / {: a; n; p; ba};" parte, eu estou muito ciente de que é diferente. Eu o modifiquei para o novo formato de etiqueta (também conhecido como # label # vs: label em outra resposta que estava travando o script em alguns casos). Você tem algum problema válido com a minha resposta (como falha no script ou sintaxe incorreta) ou simplesmente -1 respondeu a minha resposta porque pensou "Não sei como recortar e colar"?
Eu descobri uma maneira de fazer isso usando funções.
Digamos, por exemplo, você tem 3 opções: A, B, e C. Ae Bexecute um comando, mas Cfornece mais informações e o leva ao prompt original novamente. Isso pode ser feito usando funções.
Observe que, como a linha que contém function demoFunctionestá apenas configurando a função, é necessário chamar demoFunctionapós esse script para que a função realmente seja executada.
Você pode adaptar isso facilmente escrevendo várias outras funções e chamando-as se " GOTO" precisar de outro lugar no seu script de shell.
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
no bash (pelo menos dizcommand not found
para mim). Por quê? Provavelmente, existe uma maneira melhor de fazê-lo.goto
instrução pule muito código para depurar um script grande sem esperar uma hora para que várias tarefas não relacionadas sejam concluídas. Eu certamente não usaria umgoto
no código de produção, mas, para depurar meu código, tornaria minha vida infinitamente mais fácil, e seria mais fácil identificar quando se tratava de removê-lo.Respostas:
Não, não há; ver §3.2.4 "Comandos compostos" na Bash Manual de Referência para obter informações sobre as estruturas de controle que fazer existir. Em particular, observe a menção de
break
econtinue
, que não é tão flexível quantogoto
, mas é mais flexível no Bash do que em alguns idiomas, e pode ajudá-lo a alcançar o que deseja. (O que você quiser.)fonte
break
ou acontinue
partir do loop interno, enquanto o Bash permite especificar quantos níveis de loop serão saltos. (E mesmo em idiomas que permitembreak
oucontinue
de loops arbitrários, a maioria exige que seja expresso estaticamente - por exemplo,break foo;
sairá do loop rotuladofoo
- enquanto no Bash é expresso dinamicamente - por exemplo,break "$foo"
sairá de$foo
loops. )break LABEL_NAME;
, em vez do nojento de Bashbreak INTEGER;
.Se você o estiver usando para pular parte de um script grande para depuração (consulte o comentário de Karl Nicoll), se false puder ser uma boa opção (não tenho certeza se "false" está sempre disponível, para mim está em / bin / false) :
A dificuldade surge quando é hora de extrair seu código de depuração. A construção "se falsa" é bem direta e memorável, mas como você encontra o fi correspondente? Se o seu editor permitir bloquear o recuo, você poderá recuar o bloco ignorado (será necessário recuperá-lo quando terminar). Ou um comentário sobre a linha fi, mas teria que ser algo que você se lembraria, que eu suspeito que seja muito dependente do programador.
fonte
false
está sempre disponível. Mas se você tem um bloco de código que não deseja executar, basta comentar. Ou exclua-o (e procure no seu sistema de controle de origem se precisar recuperá-lo mais tarde).Na verdade, pode ser útil para algumas necessidades de depuração ou demonstração.
Descobri que a solução Bob Copeland http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegante:
resulta em:
fonte
: start:
para que não sejam erros de sintaxe.set -x
ajuda a entender o que está acontecendoVocê pode usar
case
no bash para simular um goto:produz:
fonte
bash v4.0+
. No entanto, não é uma opção de uso geral,goto
mas uma opção indireta para acase
declaração.Se você estiver testando / depurando um script bash e simplesmente quiser pular adiante uma ou mais seções de código, aqui está uma maneira muito simples de fazer isso, que também é muito fácil de encontrar e remover mais tarde (diferente da maioria dos métodos descrito acima).
Para colocar seu script de volta ao normal, exclua todas as linhas com
GOTO
.Também podemos prettificar esta solução, adicionando um
goto
comando como um alias:Os aliases geralmente não funcionam em scripts bash, por isso precisamos do
shopt
comando para corrigir isso.Se você deseja ativar / desativar os seus
goto
, precisamos de um pouco mais:Então você pode fazer
export DEBUG=TRUE
antes de executar o script.Os rótulos são comentários, portanto, não causam erros de sintaxe se desativarmos os nossos
goto
(definindogoto
como ':
' não-op ' '), mas isso significa que precisamos citá-los em nossogoto
declarações.Sempre que usar qualquer tipo de
goto
solução, você deve tomar cuidado para que o código que você está passando não defina nenhuma variável em que você confia mais tarde - talvez seja necessário mover essas definições para a parte superior do seu script, ou logo acima de uma de suasgoto
declarações.fonte
if(false)
parece fazer mais sentido (para mim).alias goto=":<<"
e dispensarcat
completamente.Embora outros já tenham esclarecido que não há
goto
equivalente direto no bash (e tenham fornecido as alternativas mais próximas, como funções, loops e interrupção), gostaria de ilustrar como o uso de um loop plusbreak
pode simular um tipo específico de instrução goto.A situação em que acho isso mais útil é quando preciso retornar ao início de uma seção de código se determinadas condições não forem atendidas. No exemplo abaixo, o loop while será executado para sempre até o ping parar de soltar pacotes para um IP de teste.
fonte
Esta solução teve os seguintes problemas:
:
label:
qualquer lugar da linha como um rótuloAqui está uma
shell-check
versão fixa ( limpa):fonte
Há mais uma capacidade de obter os resultados desejados: comando
trap
. Pode ser usado para fins de limpeza, por exemplo.fonte
Não há
goto
no bash.Aqui está uma solução alternativa suja usando
trap
qual pula apenas para trás :)Resultado:
Isso não deve ser usado dessa maneira, mas apenas para fins educacionais. Aqui está o porquê disso funcionar:
trap
está usando o tratamento de exceções para obter a alteração no fluxo de código. Nesse caso,trap
está capturando qualquer coisa que faça com que o script EXIT. O comandogoto
não existe e, portanto, gera um erro, que normalmente sai do script. Este erro está sendo detectadotrap
e2>/dev/null
oculta a mensagem de erro que normalmente seria exibida.Obviamente, essa implementação do goto não é confiável, pois qualquer comando inexistente (ou qualquer outro erro, dessa maneira) executaria o mesmo comando trap. Em particular, você não pode escolher para qual rótulo ir.
Basicamente, no cenário real, você não precisa de nenhuma instrução goto, elas são redundantes, pois chamadas aleatórias para lugares diferentes dificultam a compreensão do seu código.
Se seu código for chamado várias vezes, considere usar loop e altere seu fluxo de trabalho para usar
continue
ebreak
.Se o seu código se repetir, considere escrever a função e chamá-la quantas vezes quiser.
Se seu código precisar pular para uma seção específica com base no valor da variável, considere usar a
case
instruçãoSe você pode separar seu código longo em partes menores, considere movê-lo para arquivos separados e chame-os a partir do script pai.
fonte
trap
em usarexception handling
para conseguir a mudança no fluxo de código. Nesse caso, a armadilha está capturando tudo o que faz com que o scriptEXIT
seja acionado, invocando o comando inexistentegoto
. BTW: O argumentogoto trap
pode ser qualquer coisa,goto ignored
porque é ogoto
que causa a EXIT e2>/dev/null
oculta a mensagem de erro informando que seu script está saindo.Esta é uma pequena correção do script de Judy Schmidt apresentado por Hubbbitus.
Colocar rótulos não escapados no script foi problemático na máquina e causou uma falha. Isso foi fácil de resolver, adicionando # para escapar dos rótulos. Obrigado a Alexej Magura e access_granted por suas sugestões.
fonte
Um simples ir pesquisável para o uso de comentar blocos de código durante a depuração.
O resultado é-> GOTO concluído
fonte
Eu descobri uma maneira de fazer isso usando funções.
Digamos, por exemplo, você tem 3 opções:
A
,B
, eC
.A
eB
execute um comando, masC
fornece mais informações e o leva ao prompt original novamente. Isso pode ser feito usando funções.Observe que, como a linha que contém
function demoFunction
está apenas configurando a função, é necessário chamardemoFunction
após esse script para que a função realmente seja executada.Você pode adaptar isso facilmente escrevendo várias outras funções e chamando-as se "
GOTO
" precisar de outro lugar no seu script de shell.fonte