Caso de falla basado en si condición

11

Estoy buscando una manera de que ocurra una falla basada en una condición if dentro de una condición de caso en bash. Por ejemplo:

input="foo"
VAR="1"

case $input in
foo)
    if [ $VAR = "1" ]; then

        # perform fallthrough

    else

        # do not perform fallthrough

    fi
;;
*)
    echo "fallthrough worked!"
;;
esac

En el código anterior, si la variable VARes 1, me gustaría que la condición del caso se realizara de forma fallida.

Smashgen
fuente
1
Pequeña pregunta: ¿Estás tratando de saltar de una if [ $VAR -eq 1 ]; thenparte del código a lo que sea que esté *)? Porque eso es completamente diferente de lo que se llama fallthrough, lo que hace que la formulación de su pregunta sea un poco engañosa.
Sergiy Kolodyazhnyy

Respuestas:

8

No puedes La forma de tener una casecaída es reemplazar el ;;separador con ;&(o ;;&). Y es un error de sintaxis poner eso dentro de un if.

Podría escribir toda la lógica como un condicional regular:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
ilkkachu
fuente
¡Sí puede ! :)
NotAnUnixNazi
@Isaac, bueno, sí, aunque especificaron basar el fracaso en un if.
ilkkachu
+1 Para ser claros: voté tu respuesta. :).
NotAnUnixNazi
7

Sugeriría reestructurar su lógica: coloque el código "fallthrough" en una función en su lugar:

fallthrough() { echo 'fallthrough worked!'; }

for input in foo bar; do
    for var in 1 2; do
        echo "$input $var"
        case $input in
            foo)
                if (( var == 1 )); then
                    echo "falling through"
                    fallthrough
                else
                    echo "not falling through"
                fi
            ;;
            *) fallthrough;;
        esac
    done
done

salidas

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!
Glenn Jackman
fuente
7

La siguiente secuencia de comandos convierte su prueba "de adentro hacia afuera" en el sentido de que $varprimero probamos y luego realizamos el ensayo (usando ;&en a case) dependiendo de $input.

Hacemos esto porque la cuestión de si "realizar la falla" o no depende realmente de $inputsi $vares así 1. Si se trata de algún otro valor, la pregunta de si se debe hacer la falla ni siquiera tiene que hacerse.

#/bin/bash

input='foo'
var='1'

case $var in
    1)
        case $input in
            foo)
                echo 'perform fallthrough'
                ;&
            *)
                echo 'fallthough worked'
        esac
        ;;
    *)
        echo 'what fallthrough?'
esac

O sin case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
Kusalananda
fuente
Creo que has clavado lo que OP realmente quería, que parece estar saltando de su declaración if original a lo que sea *). Ya tengo mi +1.
Sergiy Kolodyazhnyy
5

No es algo que haría, pero podrías lograr algo que se acerque con:

shopt -s extglob # for !(*)
default='*'
case $input in
  (foo)
    if [ "$VAR" = 1 ]; then
      echo going for fallthrough
    else
      echo disabling fallthrough
      default='!(*)'
    fi ;;&

  ($default)
    echo fallthrough
esac
Stéphane Chazelas
fuente
2

Pruebe ambas variables a la vez (bash 4.0-alpha +):

#!/bin/bash
while (($#>1)); do
    input=$1    VAR=$2
    echo "input=${input} VAR=${VAR}"; shift 2

    if [ "$VAR" = 1 ]; then new=1; else new=0; fi

    case $input$new in
    foo0)   echo "do not perform fallthrough"   ;;
    foo*)   echo "perform fallthrough"          ;&
    *)      echo "fallthrough worked!"          ;;
    esac

    echo
done

En pruebas:

$ ./script foo 0   foo 1   bar baz
input=foo VAR=0
do not perform fallthrough

input=foo VAR=1
perform fallthrough
fallthrough worked!

input=bar VAR=baz
fallthrough worked!

Limpio y sencillo.

Comprenda que el valor probado ( $new) debe tener solo dos valores posibles, es por eso que la cláusula if está ahí, para transformar VAR en un valor booleano. Si se puede hacer que VAR sea un booleano, pruebe 0(no 1) en el casey quite el if.

NotAnUnixNazi
fuente
1

Puede hacer que el avance sea predeterminado, pero coloque una condición que el código solo se ejecuta si se cumple la condición

#!/bin/bash

input='foo'
var='1'

case $input in
foo)
        echo "Do fall through"
;& #always fall through
*)
        if [ $var = "1" ] #execute only if condition matches
        then
        echo "fallthrough took place"
        fi
esac

Pero como ilkkachu sugirió, también puedes usar condiciones en lugar de cambiar.

yashC
fuente
1

Si no le importa que alguien se queje de que no entiende su código, simplemente puede cambiar el orden de los dos condicionales:

input="foo"
VAR="1"

if 
    case $input in
    foo)
        [ $VAR = "1" ]
    ;;
    esac
then
    echo "fallthrough worked!"
fi

O:

input="foo"
VAR="1"

case $input in
foo)
    [ $VAR = "1" ]
;;
esac &&
    echo "fallthrough worked!"

Simple y claro (al menos para mí). caseno admite fallthrough sí mismo. Pero puede reemplazar *)con &&after esacpara que respete los valores de retorno de otras ramas.

usuario23013
fuente
-4

Los nuevos ;&y los ;;&operadores se introdujeron en Bash 4.0 y, aunque ambos podrían ser útiles en situaciones similares, creo que no son de utilidad en su caso. Esto es lo que man bashdice acerca de estos operadores:

Si el ;; se utiliza el operador, no se intentan coincidencias posteriores después de la primera coincidencia de patrón. Usando; & en lugar de ;; hace que la ejecución continúe con la lista asociada con el siguiente conjunto de patrones. Usando ;; & en lugar de ;; hace que el shell pruebe la siguiente lista de patrones en la instrucción, si la hay, y ejecute cualquier lista asociada en una coincidencia exitosa.

En otras palabras, ;&es una caída a través y como la conocemos desde Cy ;;&marcas bashde verificación restantes casos en lugar de volver a casebloquear por completo. Puede encontrar un buen ejemplo de ;;&en acción aquí: /programming//a/24544780/3691891 .

Una vez dicho esto, ni ;&tampoco ;;&se podría utilizar en el script porque ambos irían a *)que sería siempre funcionan.

El siguiente script funciona y hace lo que quiere sin reorganizar la lógica, pero considérelo solo como un ejemplo y nunca confíe en él, es demasiado frágil. He tomado la idea de aquí :

#!/usr/bin/env bash

function jumpto
{
    label=$1
    cmd=$(sed -n "/$label:/{:a;n;p;ba};" "$0" | grep -v ':$')
    cmd=$(echo "$cmd" | sed 's,;;,,')
    cmd=$(echo "$cmd" | sed 's,esac,,')
    eval "$cmd"
}

input="foo"
VAR="1"

case $input in
foo)
    if [ $VAR = "1" ]; then

        printf "perform fallthrough\n"
        jumpto ft
    else
        printf "do not perform fallthrough\n"

    fi
;;
*)
    ft:
    echo "fallthrough worked!"
;;
esac
Arkadiusz Drabczyk
fuente
¿Por qué un voto negativo?
Arkadiusz Drabczyk
1
Esto puede funcionar en un ejemplo de juguete, pero es casi incomprensible y no funcionaría en un script más grande. La forma de simular goto es extremadamente frágil, y decir que realmente simula goto es una exageración. El código vuelve de la jumptofunción y ejecuta lo que viene después. El hecho de que esto sea equivalente a continuar ejecutando después del bloque entre ft:y ;;es una coincidencia porque jumptoes el último comando en el mismo comando complejo que el bloque saltado.
Gilles 'SO- deja de ser malvado'
1
Bien, claramente enfaticé eso en mi respuesta.
Arkadiusz Drabczyk