Analiza przypadku na podstawie warunku if

11

Szukam sposobu, aby nastąpił upadek w oparciu o warunek if w warunku case w bash. Na przykład:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

W powyższym kodzie, jeśli zmienna VARjest 1, chciałbym, aby warunek case wykonał przewrót.

Smashgen
źródło
1
Małe pytanie: Czy próbujesz przejść od if [ $VAR -eq 1 ]; thenczęści kodu do tego, co jest w środku *)? Ponieważ to całkowicie różni się od tego, co nazywa się upadkiem, dlatego frazowanie pytania jest nieco mylące.
Sergiy Kolodyazhnyy

Odpowiedzi:

8

Nie możesz Sposobem na caseupadek jest zastąpienie ;;separatora ;&(lub ;;&). I to błąd składniowy, aby umieścić to w if.

Możesz zapisać całą logikę jako zwykły warunek:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
ilkkachu
źródło
Tak, może ! :)
Izaak
@Isaac, cóż, tak, chociaż określili oparcie tego przełomu na if.
ilkkachu
+1 Aby być jasnym: głosowałem za twoją odpowiedzią. :)
Izaak
7

Sugerowałbym restrukturyzację logiki: zamiast tego umieść kod „awaryjny” w funkcji:

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

wyjścia

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!
Glenn Jackman
źródło
7

Poniższy skrypt zamienia twój test „na lewą stronę” w tym sensie, że $varnajpierw testujemy, a następnie przeprowadzamy przegląd (użycie ;&w case) w zależności od $input.

Robimy to, ponieważ pytanie, czy „przeprowadzić upadek” zależy od tego, $inputczy tak $varjest 1. Jeśli jest to jakaś inna wartość, nie trzeba nawet zadawać pytania, czy dokonać przełomu.

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

Lub bez case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
Kusalananda
źródło
Myślę, że przybiłeś to, czego naprawdę chciał OP, co wydaje się przeskakiwać z ich oryginalnego oświadczenia if na cokolwiek *). Mam już +1.
Sergiy Kolodyazhnyy
5

Nie coś, co bym zrobił, ale można osiągnąć coś zbliżającego się:

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
źródło
2

Przetestuj obie zmienne jednocześnie (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

Podczas testowania:

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

Czysty i prosty.

Zrozum, że testowana wartość ( $new) musi mieć tylko dwie możliwe wartości, dlatego istnieje klauzula if, aby przekształcić VAR w wartość logiczną. Jeśli VAR może być ustawiony jako logiczny, to sprawdź 0(nie 1) w casei usuń if.

Izaak
źródło
1

Możesz ustawić domyślną wartość domyślną, ale ustawić warunek, aby kod był wykonywany tylko wtedy, gdy warunek jest spełniony

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

Ale jak sugeruje ilkkachu, możesz również używać warunków zamiast przełączać.

yashC
źródło
1

Jeśli nie przeszkadza Ci, że ktoś narzeka, że ​​nie rozumie Twojego kodu, możesz po prostu zmienić kolejność dwóch warunków warunkowych:

input="foo"
VAR="1"

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

Lub:

input="foo"
VAR="1"

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

Proste i jasne (przynajmniej dla mnie). casenie obsługuje samego upadku. Ale można zastąpić *)z &&PO esac, aby w odniesieniu do wartości zwracanej z innych oddziałów.

użytkownik23013
źródło
-4

Nowe ;&i ;;&operatory zostały wprowadzone w Bash 4.0 i chociaż oba mogą być przydatne w podobnych sytuacjach, myślę, że nie są one przydatne w twoim przypadku. Oto, co man bashmówi o tych operatorach:

Jeśli ;; operator jest używany, po pierwszym dopasowaniu wzorca nie są podejmowane żadne kolejne dopasowania. Używanie; & zamiast ;; powoduje kontynuowanie wykonywania listy powiązanej z następnym zestawem wzorców. Używanie ;; & zamiast ;; powoduje, że powłoka testuje następną listę wzorców w instrukcji, jeśli taka istnieje, i wykonuje dowolną powiązaną listę w przypadku pomyślnego dopasowania.

Innymi słowy, ;&jest to spadek po a jak wiemy to od Ci ;;&marki bashsprawdzić pozostałych przypadkach zamiast powrocie z casebloku całkowicie. Dobry przykład ;;&działania można znaleźć tutaj: /programming//a/24544780/3691891 .

To powiedziawszy, ani ;&ani nie ;;&mogą być użyte w twoim skrypcie, ponieważ obaj przejdą do *)tego, że zawsze będzie uruchamiany.

Poniższy skrypt działa i robi, co chcesz, bez ponownego układania logiki, ale traktuj to tylko jako przykład i nigdy nie polegaj na nim, jest zbyt delikatny. Pomysł wziąłem stąd :

#!/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
źródło
Dlaczego głosowanie negatywne?
Arkadiusz Drabczyk
1
Może to działać na przykładzie zabawki, ale jest prawie niezrozumiałe i nie działałoby w większym skrypcie. Sposób, w jaki symulujesz goto, jest wyjątkowo delikatny, a stwierdzenie, że tak naprawdę symuluje goto, jest przesadą. Kod wraca z jumptofunkcji i wykonuje wszystko, co nastąpi po niej. Fakt, że jest to równoważne z kontynuowaniem wykonywania po bloku pomiędzy ft:i ;;jest zbiegiem okoliczności, ponieważ jumptojest to ostatnie polecenie w tym samym złożonym poleceniu co przeskakujący blok.
Gilles „SO - przestań być zły”
1
OK, wyraźnie to podkreśliłem w mojej odpowiedzi.
Arkadiusz Drabczyk