¿Hay una statement “goto” en bash?

¿Hay una statement “goto” en bash? Sé que se considera una mala práctica, pero necesito específicamente “goto”.

No no hay; ver §3.2.4 “Comandos compuestos” en el Manual de referencia de Bash para obtener información sobre las estructuras de control que existen. En particular, tenga en cuenta la mención de break y 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 …)

Si lo está usando para omitir parte de una secuencia de comandos grande para depuración (vea el comentario de Karl Nicoll), entonces, si es falso podría ser una buena opción (no estoy seguro de si “falso” está siempre disponible, para mí está en / bin / falso) :

 # ... Code I want to run here ... if false; then # ... Code I want to skip here ... fi # ... I want to resume here ... 

La dificultad aparece cuando es hora de arrancar el código de depuración. El constructo “si es falso” es bastante sencillo y memorable, pero ¿cómo se encuentra la combinación de fi? Si su editor le permite bloquear la sangría, puede sangrar el bloque omitido (luego querrá volver a colocarlo cuando haya terminado). O un comentario en la línea, pero tendría que ser algo que recordarás, que sospecho será muy dependiente del progtwigdor.

De hecho, puede ser útil para algunas necesidades de depuración o demostración.

Encontré esa solución de Bob Copeland http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:

 #!/bin/bash # include this boilerplate function jumpto { label=$1 cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | 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 

resultados en:

 $ ./test.sh x is 100 $ ./test.sh foo x is 10 $ ./test.sh mid This is not printed! x is 101 

Puedes usar case in bash para simular un goto:

 #!/bin/bash case bar in foo) echo foo ;& bar) echo bar ;& *) echo star ;; esac 

produce:

 bar star 

Aunque otros ya han aclarado que no hay un equivalente goto directo en bash (y proporcionaron las alternativas más cercanas, como funciones, bucles y ruptura), me gustaría ilustrar cómo el uso de un bucle plus puede simular un tipo específico de statement goto.

La situación en la que creo que es más útil es cuando necesito regresar al comienzo de una sección de código si no se cumplen ciertas condiciones. En el ejemplo a continuación, el ciclo while se ejecutará por siempre hasta que el ping deje de arrojar paquetes a una IP de prueba.

 #!/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 restred" break else echo "No connectivity" fi done 

Si está probando / depurando un script bash, y simplemente quiere saltear adelante 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 adelante (a diferencia de la mayoría de los métodos) descrito arriba).

 #!/bin/bash echo "Run this" cat >/dev/null < /dev/null <  

Para volver a poner el script en orden, simplemente elimine cualquier línea con GOTO .

También podemos embellecer esta solución, agregando un comando goto 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 bash, por lo que necesitamos el comando shopt para solucionarlo.

Si desea poder activar / desactivar sus goto 's, 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" 

Luego puede export DEBUG=TRUE antes de ejecutar el script.

Las tags son comentarios, por lo que no provocarán errores de syntax si se desactivan nuestros goto (estableciendo goto a la goto ' : ' no operativa), pero esto significa que debemos citarlos en nuestras declaraciones goto .

Siempre que utilice cualquier tipo de solución goto , debe tener cuidado de que el código que está pasando no establezca ninguna variable de la que dependa más adelante; es posible que deba mover esas definiciones al principio de su script, o justo arriba una de tus declaraciones goto .

Hay una habilidad más para lograr los resultados deseados: trap comando. Se puede usar para fines de limpieza, por ejemplo.

No hay goto en bash.

Aquí hay una solución sucia utilizando una trap que salta 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. He aquí por qué esto funciona:

trap usa el manejo de excepciones para lograr el cambio en el flujo de código. En este caso, la trap atrapa todo lo que causa que la secuencia de comandos salga. El comando goto no existe, y por lo tanto arroja un error, que normalmente saldría del script. Este error se captura con trap , y el 2>/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, por esa razón) ejecutaría el mismo comando de captura. En particular, no puede elegir a qué etiqueta ir.


Básicamente en el escenario real no necesitas ninguna statement goto, son redundantes ya que las llamadas aleatorias a diferentes lugares solo hacen que tu código sea difícil de entender.

Si su código es invocado muchas veces, entonces considere usar loop y cambiar su flujo de trabajo para usar continue y 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, entonces considere usar la statement de case .

Si puede separar su código largo en partes más pequeñas, considere moverlo a archivos separados y llamarlos desde el script principal.

Descubrí una forma de hacer esto usando funciones.

Digamos, por ejemplo, que tiene 3 opciones: A , B y C A y B ejecutan un comando, pero C le da más información y lo lleva al indicador original nuevamente. Esto se puede hacer usando funciones.

Tenga en cuenta que debido a que la function demoFunction línea demoFunction function demoFunction está configurando la función, debe llamar a demoFunction después de esa secuencia de comandos para que la función realmente se ejecute.

Puede adaptar esto fácilmente escribiendo muchas otras funciones y llamándolas si necesita ” GOTO ” en otro lugar de 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 

Esta es una pequeña corrección del guión de Judy Schmidt presentado por Hubbbitus.

Colocar tags no escapadas en la secuencia de comandos fue problemático en la máquina y causó su locking. Esto fue bastante fácil de resolver agregando # para escapar de las tags. Gracias a Alexej Magura y access_granted por sus sugerencias.

 #!/bin/bash # include this boilerplate function 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..."