Falldurchfall basierend auf der if-Bedingung

11

Ich suche nach einer Möglichkeit, ein Durchfallen zu ermöglichen, basierend auf einer if-Bedingung innerhalb einer Fallbedingung in bash. Beispielsweise:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

Wenn die Variable im obigen Code VARlautet 1, möchte ich, dass die Fallbedingung durchfällt.

Smashgen
quelle
1
Kleine Frage: Versuchen Sie, von einem if [ $VAR -eq 1 ]; thenTeil des Codes zu dem zu springen , was sich darin befindet *)? Denn das ist völlig anders als das, was Fallthrough genannt wird, was Ihre Fragestellung nur leicht irreführend macht.
Sergiy Kolodyazhnyy

Antworten:

8

Das kannst du nicht. Der Weg zum caseDurchfallen besteht darin, den ;;Separator durch ;&(oder ;;&) zu ersetzen . Und es ist ein Syntaxfehler, das in ein if zu setzen.

Sie könnten die gesamte Logik als reguläre Bedingung ausschreiben:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
ilkkachu
quelle
Ja, er kann ! :)
Isaac
@Isaac, na ja, obwohl sie angegeben haben, dass der Fallthrough auf einem basiert if.
Ilkkachu
+1 Um es klar zu sagen: Ich habe Ihre Antwort gewählt. :).
Isaac
7

Ich würde vorschlagen, Ihre Logik neu zu strukturieren: Fügen Sie stattdessen den "Fallthrough" -Code in eine Funktion ein:

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

Ausgänge

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

Das folgende Skript dreht Ihren Test in dem Sinne "von innen nach außen", dass wir $varzuerst testen und dann den Fallthrough (unter Verwendung ;&von a case) abhängig von durchführen $input.

Wir tun dies, weil die Frage, ob "der Fallthrough" durchgeführt werden soll oder nicht, wirklich nur davon abhängt, $inputob dies der Fall $varist 1. Wenn es sich um einen anderen Wert handelt, muss nicht einmal die Frage gestellt werden, ob der Fallthrough durchgeführt werden soll.

#/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

Oder ohne case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
Kusalananda
quelle
Ich denke, Sie haben genau das gefunden, was OP eigentlich wollte, was von ihrer ursprünglichen if-Aussage zu was auch immer zu springen scheint *). Habe schon meine +1.
Sergiy Kolodyazhnyy
5

Nicht etwas, was ich tun würde, aber Sie könnten etwas erreichen, das sich nähert mit:

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
quelle
2

Testen Sie beide Variablen gleichzeitig (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

Beim Testen:

$ ./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!

Sauber und einfach.

Verstehen Sie, dass der getestete Wert ( $new) nur zwei mögliche Werte haben darf. Deshalb ist die if-Klausel vorhanden, um VAR in einen Booleschen Wert umzuwandeln. Wenn VAR als Boolescher Wert festgelegt werden kann, testen Sie das 0(nicht 1) caseund entfernen Sie das if.

Isaac
quelle
1

Sie können den Fallthrough-Standard festlegen, aber eine Bedingung festlegen, dass der Code nur ausgeführt wird, wenn die Bedingung erfüllt ist

#!/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

Aber wie ilkkachu vorgeschlagen hat, können Sie auch Bedingungen verwenden, anstatt zu wechseln.

yashC
quelle
1

Wenn es Ihnen nichts ausmacht, wenn sich jemand darüber beschwert, dass er Ihren Code nicht versteht, können Sie einfach die Reihenfolge der beiden Bedingungen ändern:

input="foo"
VAR="1"

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

Oder:

input="foo"
VAR="1"

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

Einfach und klar (zumindest für mich). caseunterstützt Fallthrough selbst nicht. Aber Sie können ersetzen *)mit &&nach esacihm auf die Rückgabewerte anderer Zweige machen zu respektieren.

user23013
quelle
-4

Neu ;&und ;;&Operatoren wurden in Bash 4.0 eingeführt und obwohl beide in ähnlichen Situationen nützlich sein könnten, denke ich, dass sie in Ihrem Fall keinen Nutzen haben. Dies ist, was man bashüber diese Operatoren sagt:

Wenn die ;; Wenn der Operator verwendet wird, werden nach der ersten Musterübereinstimmung keine nachfolgenden Übereinstimmungen versucht. Verwenden von; & anstelle von ;; bewirkt, dass die Ausführung mit der Liste fortgesetzt wird, die dem nächsten Satz von Mustern zugeordnet ist. Verwenden von ;; & anstelle von ;; veranlasst die Shell, die nächste Musterliste in der Anweisung zu testen, falls vorhanden, und eine zugehörige Liste für eine erfolgreiche Übereinstimmung auszuführen.

Mit anderen Worten, ;&ist ein Sturz durch und wie wir es aus wissen Cund ;;&macht bashüberprüfen übrigen Fälle stattdessen von der Rückkehr caseBlock vollständig. Ein schönes Beispiel für eine ;;&Aktion finden Sie hier: /programming//a/24544780/3691891 .

Davon abgesehen konnte weder ;&noch ;;&in Ihrem Skript verwendet werden, da beide *)dazu immer ausgeführt würden.

Das folgende Skript funktioniert und macht, was Sie wollen, ohne die Logik neu zu ordnen, aber betrachten Sie es nur als Beispiel und verlassen Sie sich nie darauf, es ist zu zerbrechlich. Ich habe die Idee von hier genommen :

#!/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
quelle
Warum eine Ablehnung?
Arkadiusz Drabczyk
1
Dies mag in einem Spielzeugbeispiel funktionieren, ist aber nahezu unverständlich und würde in einem größeren Skript nicht funktionieren. Die Art und Weise, wie Sie goto simulieren, ist äußerst fragil, und zu sagen, dass es goto wirklich simuliert, ist übertrieben. Der Code kehrt von der jumptoFunktion zurück und führt alles aus, was danach kommt. Die Tatsache, dass dies gleichbedeutend ist mit der Fortsetzung der Ausführung nach dem Block zwischen ft:und ;;ist ein Zufall, da dies jumptoder letzte Befehl in demselben komplexen Befehl ist wie der Sprungblock.
Gilles 'SO - hör auf böse zu sein'
1
OK, das habe ich in meiner Antwort deutlich betont.
Arkadiusz Drabczyk