linux - variable - ¿Hay una declaración "goto" en bash?
¿cuál es la forma correcta de guardar el directorio actual a una variable? (10)
¿Hay una declaración "goto" en bash? Sé que se considera una mala práctica, pero necesito específicamente "goto".
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 declaración 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 restored"
break
else
echo "No connectivity"
fi
done
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
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 etiquetas no escapadas en la secuencia de comandos fue problemático en la máquina y causó su bloqueo. 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 boilerplate
function goto {
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | grep -v '':$'')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $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..."
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 declaración 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 declaración 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.
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 sí 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 ...)
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
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 <<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 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 etiquetas son comentarios, por lo que no provocarán errores de sintaxis 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
.
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 programador.