No, no hay gotoen bash (al menos lo dice command not foundpara mí). ¿Por qué? Lo más probable es que haya una mejor manera de hacerlo.
Niklas B.
153
Él puede tener sus razones. Encontré esta pregunta porque quiero que una gotodeclaración omita mucho código para depurar un script grande sin esperar una hora para que se completen varias tareas no relacionadas. Ciertamente no usaría un gotoen el código de producción, pero para depurar mi código, haría mi vida infinitamente más fácil, y sería más fácil detectarlo cuando se tratara de eliminarlo.
Karl Nicoll el
30
@delnan Pero no tener goto puede hacer que algunas cosas sean más complicadas. De hecho, hay casos de uso.
glglgl
72
Estoy harto de este mito goto! ¡No hay nada malo en ir a Goto! Todo lo que escribes eventualmente se convierte en goto. En ensamblador, solo hay goto. Una buena razón para usar goto en lenguajes de programación superiores es, por ejemplo, saltar de bucles anidados de una manera limpia y legible.
Alexander Czutro
25
"Evitar goto" es una gran regla. Como cualquier regla, se debe aprender en tres fases. Primero: sigue la regla hasta que sea una segunda naturaleza. Segundo, aprenda a entender las razones de la regla. Tercero, aprenda buenas excepciones a la regla, basándose en una comprensión integral de cómo seguir la regla y los motivos de la regla. Evite omitir los pasos anteriores, que serían como usar "goto". ;-)
Jeff Learman
Respuestas:
75
No no hay; ver §3.2.4 "Comandos de compuestos" en la Referencia Bash manual para obtener información sobre las estructuras de control que hacen existir. En particular, tenga en cuenta la mención de breaky continue, que no son tan flexibles como goto, pero son más flexibles en Bash que en algunos idiomas, y pueden ayudarlo a lograr lo que desea. (Lo que sea que quieras ...)
¿Podría ampliar "más flexible en Bash que en algunos idiomas"?
user239558
21
@ user239558: Algunos idiomas sólo le permiten al breako continuedesde el bucle más interior, mientras que Bash le permite especificar cuántos niveles de bucle para saltar. (E incluso de los idiomas que le permiten breako continuedesde bucles arbitrarios, la mayoría requiere que se expresen estáticamente, por ejemplo, break foo;saldrán del bucle etiquetado foo, mientras que en Bash se expresará dinámicamente, por ejemplo, break "$foo"saldrán de $foobucles. )
ruakh
Recomiendo definir funciones con las características necesarias y llamarlas desde otras partes del código.
blmayer
@ruakh En realidad, muchos idiomas permiten break LABEL_NAME;, en lugar de asqueroso Bash break INTEGER;.
Sapphire_Brick
1
@Sapphire_Brick: Sí, lo mencioné en el comentario al que estás respondiendo.
ruakh
125
Si lo está utilizando para omitir parte de un script grande para la depuración (vea el comentario de Karl Nicoll), entonces si false podría ser una buena opción (no estoy seguro si "false" siempre está disponible, para mí está en / bin / false) :
# ... Code I want to run here ...if false;then# ... Code I want to skip here ...fi# ... I want to resume here ...
La dificultad viene cuando es hora de extraer su código de depuración. El constructo "si es falso" es bastante sencillo y memorable, pero ¿cómo encuentra la coincidencia? Si su editor le permite bloquear la sangría, puede sangrar el bloque omitido (entonces querrá volver a colocarlo cuando haya terminado). O un comentario en la línea fija, pero tendría que ser algo que recordará, que sospecho que dependerá mucho del programador.
Sí falsesiempre está disponible. Pero si tiene un bloque de código que no desea ejecutar, simplemente coméntelo. O elimínelo (y busque en su sistema de control de fuente si necesita recuperarlo más tarde).
Keith Thompson
1
Si el bloque de código es demasiado largo para comentar tediosamente una línea a la vez, vea estos trucos. stackoverflow.com/questions/947897/… Sin embargo, tampoco ayudan a que un editor de texto coincida de principio a fin, por lo que no son una gran mejora.
Camille Goudeseune
44
"si es falso" suele ser mucho mejor que comentar el código, ya que garantiza que el código adjunto siga siendo código legal. La mejor excusa para comentar el código es cuando realmente necesita eliminarse, pero hay algo que debe recordarse, por lo que es solo un comentario y ya no es "código".
Jeff Learman
1
Yo uso el comentario '#if false;'. De esa forma, puedo buscar eso y encontrar el principio y el final de la sección de eliminación de depuración.
Jon
Esta respuesta no debe ser votada ya que no responde a la pregunta del OP de ninguna manera.
Viktor Joras
54
De hecho, puede ser útil para algunas necesidades de depuración o demostración.
Tenga en cuenta que esto requiere bash v4.0+. Sin embargo, no es una gotoopción de propósito general sino una alternativa para la casedeclaración.
mklement0
3
Creo que esta debería ser la respuesta. Tengo una verdadera necesidad de ir a fin de admitir la reanudación de la ejecución de un script, a partir de una instrucción dada. Esto es, en todos los sentidos, pero la semántica, el goto, la semántica y los azúcares sintácticos son lindos, pero no estrictamente necesarios. Gran solución, OMI.
nathan g
1
@nathang, si es la respuesta depende de si su caso coincide con el subconjunto del caso general sobre el que preguntó el OP. Desafortunadamente, la pregunta se refiere al caso general, por lo que esta respuesta es demasiado estrecha para ser correcta. (Si esa pregunta debe cerrarse como demasiado amplia por esa razón es una discusión diferente).
Charles Duffy
1
goto es más que seleccionar. Ir a la función significa poder saltar a lugares según algunas condiciones, creando incluso un ciclo como el flujo ...
Sergio Abreu
19
Si está probando / depurando un script bash, y simplemente quiere saltarse una o más secciones de código, aquí hay una forma muy simple de hacerlo que también es muy fácil de encontrar y eliminar más tarde (a diferencia de la mayoría de los métodos descrito arriba).
#!/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 que su script vuelva a la normalidad, simplemente elimine las líneas con GOTO.
También podemos embellecer esta solución, agregando un gotocomando como un 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"
Los alias no suelen funcionar en scripts de bash, por lo que necesitamos el shoptcomando para solucionarlo.
Si desea habilitar / deshabilitar gotolos suyos, necesitamos un poco má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"
Entonces puedes hacerlo export DEBUG=TRUEantes de ejecutar el script.
Las etiquetas son comentarios, por lo que no causarán errores de sintaxis si deshabilita nuestro goto'(configurando gotoel' :'no-op), pero esto significa que necesitamos citarlos en nuestras gotodeclaraciones.
Siempre que use cualquier tipo de gotosolución, debe tener cuidado de que el código que está saltando no establezca ninguna variable en la que confíe más adelante; es posible que deba mover esas definiciones a la parte superior de su script, o justo por encima de una de sus gotodeclaraciones
Sin entrar en lo bueno / malo de Goto's, la solución de Laurence es simplemente genial. Tienes mi voto.
Mogens TrasherDK
1
Eso es más como un salto, if(false)parece tener más sentido (para mí).
vesperto
2
Citar la "etiqueta" de goto también significa que here-doc no se expande / evalúa, lo que le ahorra algunos errores difíciles de encontrar (sin comillas, estaría sujeto a: expansión de parámetros, sustitución de comandos y expansión aritmética, que pueden tener efectos secundarios). También puede modificar esto un poco alias goto=":<<"y prescindir por catcompleto.
señor púrpura el
sí, vesperto, puede usar una declaración 'if', y lo he hecho en muchos scripts, pero se vuelve muy complicado y es mucho más difícil de controlar y rastrear, especialmente si desea cambiar sus puntos de salto con frecuencia.
Laurence Renshaw
12
Aunque otros ya han aclarado que no hay un gotoequivalente directo en bash (y han proporcionado las alternativas más cercanas, como funciones, bucles y pausa), me gustaría ilustrar cómo usar un bucle plus breakpuede simular un tipo específico de instrucción goto.
La situación en la que encuentro esto más útil es cuando necesito volver al comienzo de una sección de código si no se cumplen ciertas condiciones. En el siguiente ejemplo, el ciclo while se ejecutará para siempre hasta que el ping deje de soltar paquetes a una IP de prueba.
#!/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
Aquí hay algunas soluciones sucias trapque usan saltos solo hacia atrás :)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2>/dev/null
echo bar
Salida:
$ ./test.sh
foo
I am
here now.
Esto no debe usarse de esa manera, sino solo con fines educativos. Aquí es por qué esto funciona:
trapestá utilizando el manejo de excepciones para lograr el cambio en el flujo de código. En este caso, trapestá detectando cualquier cosa que provoque que el script salga. El comando gotono existe, y por lo tanto arroja un error, que normalmente saldría del script. Este error se está detectando trapy 2>/dev/nulloculta el mensaje de error que normalmente se mostraría.
Esta implementación de goto obviamente no es confiable, ya que cualquier comando inexistente (o cualquier otro error, de esa manera), ejecutaría el mismo comando trap. En particular, no puede elegir a qué etiqueta ir.
Básicamente, en un escenario real, no necesita ninguna instrucción goto, son redundantes ya que las llamadas aleatorias a diferentes lugares solo hacen que su código sea difícil de entender.
Si su código se invoca muchas veces, considere usar loop y cambiar su flujo de trabajo para usar continuey break.
Si su código se repite, considere escribir la función y llamarla tantas veces como desee.
Si su código necesita saltar a una sección específica basada en el valor de la variable, considere usar la caseinstrucción.
Si puede separar su código largo en partes más pequeñas, considere moverlo a archivos separados y llamarlos desde el script principal.
¿Cuáles son las diferencias entre esta forma y una función normal?
yurenchen 01 de
2
@yurenchen: piense trapen usar exception handlingpara lograr el cambio en el flujo de código. En este caso, la trampa está atrapando cualquier cosa que provoque que el script se active EXIT, invocando el comando inexistente goto. Por cierto: el argumento goto trappodría ser cualquier cosa, goto ignoredporque es el gotoque causa la SALIDA, y 2>/dev/nulloculta el mensaje de error que dice que su script está saliendo.
Jesse Chisholm
2
Esta es una pequeña corrección del guión Judy Schmidt presentado por Hubbbitus.
Poner etiquetas sin escape en el script fue problemático en la máquina y causó que se bloqueara. Esto fue bastante fácil de resolver agregando # para escapar de las etiquetas. Gracias a Alexej Magura y access_granted por sus sugerencias.
#!/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 copia pegar no funciona => todavía hay un jumpto.
Alexis Paques
Qué significa eso? ¿Qué no funciona? ¿Podrías ser más específico?
thebunnyrules
¡Por supuesto que probé el código! Probé en 5 o 6 condiciones diferentes antes de publicar todo exitoso. ¿Cómo le falló el código? ¿Qué mensaje de error o código?
thebunnyrules
Cualquier hombre. Quiero ayudarte, pero estás siendo muy vago y poco comunicativo (sin mencionar insultante con esa pregunta de "¿lo probaste?"). ¿Qué significa "no pegaste correctamente"? Si te refieres a "/ $ # label #: / {: a; n; p; ba};" parte, soy muy consciente de que es diferente. Lo modifiqué para el nuevo formato de etiqueta (también conocido como # etiqueta # vs: etiqueta en otra respuesta que estaba bloqueando el script en algunos casos). ¿Tiene algún problema válido con mi respuesta (como bloqueo del script o sintaxis incorrecta) o simplemente -1 mi respuesta porque pensó "No sé cómo cortar y pegar"?
thebunnyrules
1
@thebunnyrules ¡Me encontré con el mismo problema que tú, pero lo resolví de forma diferente para tu solución!
Fabby
2
Un simple goto de búsqueda para el uso de comentar bloques de código al depurar.
GOTO=false
if ${GOTO};then
echo "GOTO failed"...fi# End of GOTO
echo "GOTO done"
Encontré una manera de hacer esto usando funciones.
Digamos, por ejemplo, usted tiene 3 opciones: A, B, y C. Ay Bejecuta un comando, pero Cte da más información y te lleva al indicador original nuevamente. Esto se puede hacer usando funciones.
Tenga en cuenta que dado que el contacto de la línea solo function demoFunctionestá configurando la función, debe llamar demoFunctiondespués de ese script para que la función realmente se ejecute.
Puede adaptar esto fácilmente escribiendo varias otras funciones y llamándolas si necesita " GOTO" otro lugar en su 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
en bash (al menos lo dicecommand not found
para mí). ¿Por qué? Lo más probable es que haya una mejor manera de hacerlo.goto
declaración omita mucho código para depurar un script grande sin esperar una hora para que se completen varias tareas no relacionadas. Ciertamente no usaría ungoto
en el código de producción, pero para depurar mi código, haría mi vida infinitamente más fácil, y sería más fácil detectarlo cuando se tratara de eliminarlo.Respuestas:
No no hay; ver §3.2.4 "Comandos de compuestos" en la Referencia Bash manual para obtener información sobre las estructuras de control que hacen existir. En particular, tenga en cuenta la mención de
break
ycontinue
, que no son tan flexibles comogoto
, pero son más flexibles en Bash que en algunos idiomas, y pueden ayudarlo a lograr lo que desea. (Lo que sea que quieras ...)fuente
break
ocontinue
desde el bucle más interior, mientras que Bash le permite especificar cuántos niveles de bucle para saltar. (E incluso de los idiomas que le permitenbreak
ocontinue
desde bucles arbitrarios, la mayoría requiere que se expresen estáticamente, por ejemplo,break foo;
saldrán del bucle etiquetadofoo
, mientras que en Bash se expresará dinámicamente, por ejemplo,break "$foo"
saldrán de$foo
bucles. )break LABEL_NAME;
, en lugar de asqueroso Bashbreak INTEGER;
.Si lo está utilizando para omitir parte de un script grande para la depuración (vea el comentario de Karl Nicoll), entonces si false podría ser una buena opción (no estoy seguro si "false" siempre está disponible, para mí está en / bin / false) :
La dificultad viene cuando es hora de extraer su código de depuración. El constructo "si es falso" es bastante sencillo y memorable, pero ¿cómo encuentra la coincidencia? Si su editor le permite bloquear la sangría, puede sangrar el bloque omitido (entonces querrá volver a colocarlo cuando haya terminado). O un comentario en la línea fija, pero tendría que ser algo que recordará, que sospecho que dependerá mucho del programador.
fuente
false
siempre está disponible. Pero si tiene un bloque de código que no desea ejecutar, simplemente coméntelo. O elimínelo (y busque en su sistema de control de fuente si necesita recuperarlo más tarde).De hecho, puede ser útil para algunas necesidades de depuración o demostración.
Encontré que la solución de Bob Copeland http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:
resultados en:
fuente
: start:
tal que no sean errores de sintaxis.set -x
ayuda a entender lo que está sucediendoPuedes usar
case
en bash para simular un goto:produce:
fuente
bash v4.0+
. Sin embargo, no es unagoto
opción de propósito general sino una alternativa para lacase
declaración.Si está probando / depurando un script bash, y simplemente quiere saltarse una o más secciones de código, aquí hay una forma muy simple de hacerlo que también es muy fácil de encontrar y eliminar más tarde (a diferencia de la mayoría de los métodos descrito arriba).
Para que su script vuelva a la normalidad, simplemente elimine las líneas con
GOTO
.También podemos embellecer esta solución, agregando un
goto
comando como un alias:Los alias no suelen funcionar en scripts de bash, por lo que necesitamos el
shopt
comando para solucionarlo.Si desea habilitar / deshabilitar
goto
los suyos, necesitamos un poco más:Entonces puedes hacerlo
export DEBUG=TRUE
antes de ejecutar el script.Las etiquetas son comentarios, por lo que no causarán errores de sintaxis si deshabilita nuestro
goto
'(configurandogoto
el':
'no-op), pero esto significa que necesitamos citarlos en nuestrasgoto
declaraciones.Siempre que use cualquier tipo de
goto
solución, debe tener cuidado de que el código que está saltando no establezca ninguna variable en la que confíe más adelante; es posible que deba mover esas definiciones a la parte superior de su script, o justo por encima de una de susgoto
declaracionesfuente
if(false)
parece tener más sentido (para mí).alias goto=":<<"
y prescindir porcat
completo.Aunque otros ya han aclarado que no hay un
goto
equivalente directo en bash (y han proporcionado las alternativas más cercanas, como funciones, bucles y pausa), me gustaría ilustrar cómo usar un bucle plusbreak
puede simular un tipo específico de instrucción goto.La situación en la que encuentro esto más útil es cuando necesito volver al comienzo de una sección de código si no se cumplen ciertas condiciones. En el siguiente ejemplo, el ciclo while se ejecutará para siempre hasta que el ping deje de soltar paquetes a una IP de prueba.
fuente
Esta solución tenía los siguientes problemas:
:
label:
cualquier lugar de una línea como etiquetaAquí hay una
shell-check
versión fija ( limpia):fuente
Hay una habilidad más para lograr los resultados deseados: comando
trap
. Se puede utilizar para fines de limpieza, por ejemplo.fuente
No hay
goto
en bash.Aquí hay algunas soluciones sucias
trap
que usan saltos solo hacia atrás :)Salida:
Esto no debe usarse de esa manera, sino solo con fines educativos. Aquí es por qué esto funciona:
trap
está utilizando el manejo de excepciones para lograr el cambio en el flujo de código. En este caso,trap
está detectando cualquier cosa que provoque que el script salga. El comandogoto
no existe, y por lo tanto arroja un error, que normalmente saldría del script. Este error se está detectandotrap
y2>/dev/null
oculta el mensaje de error que normalmente se mostraría.Esta implementación de goto obviamente no es confiable, ya que cualquier comando inexistente (o cualquier otro error, de esa manera), ejecutaría el mismo comando trap. En particular, no puede elegir a qué etiqueta ir.
Básicamente, en un escenario real, no necesita ninguna instrucción goto, son redundantes ya que las llamadas aleatorias a diferentes lugares solo hacen que su código sea difícil de entender.
Si su código se invoca muchas veces, considere usar loop y cambiar su flujo de trabajo para usar
continue
ybreak
.Si su código se repite, considere escribir la función y llamarla tantas veces como desee.
Si su código necesita saltar a una sección específica basada en el valor de la variable, considere usar la
case
instrucción.Si puede separar su código largo en partes más pequeñas, considere moverlo a archivos separados y llamarlos desde el script principal.
fuente
trap
en usarexception handling
para lograr el cambio en el flujo de código. En este caso, la trampa está atrapando cualquier cosa que provoque que el script se activeEXIT
, invocando el comando inexistentegoto
. Por cierto: el argumentogoto trap
podría ser cualquier cosa,goto ignored
porque es elgoto
que causa la SALIDA, y2>/dev/null
oculta el mensaje de error que dice que su script está saliendo.Esta es una pequeña corrección del guión Judy Schmidt presentado por Hubbbitus.
Poner etiquetas sin escape en el script fue problemático en la máquina y causó que se bloqueara. Esto fue bastante fácil de resolver agregando # para escapar de las etiquetas. Gracias a Alexej Magura y access_granted por sus sugerencias.
fuente
Un simple goto de búsqueda para el uso de comentar bloques de código al depurar.
El resultado es-> GOTO hecho
fuente
Encontré una manera de hacer esto usando funciones.
Digamos, por ejemplo, usted tiene 3 opciones:
A
,B
, yC
.A
yB
ejecuta un comando, peroC
te da más información y te lleva al indicador original nuevamente. Esto se puede hacer usando funciones.Tenga en cuenta que dado que el contacto de la línea solo
function demoFunction
está configurando la función, debe llamardemoFunction
después de ese script para que la función realmente se ejecute.Puede adaptar esto fácilmente escribiendo varias otras funciones y llamándolas si necesita "
GOTO
" otro lugar en su script de shell.fuente