Fallimento del caso in base alla condizione if


11

Sto cercando un modo per far accadere il fallthrough basato su una condizione if all'interno di una condizione del caso in bash. Per esempio:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

Nel codice sopra, se la variabile VARè 1, vorrei che la condizione del caso eseguisse il fallthrough.


1
Piccola domanda: stai provando a passare da una if [ $VAR -eq 1 ]; thenparte del codice a qualsiasi cosa sia contenuta *)? Perché è completamente diverso da quello che viene chiamato fallthrough, rendendo così la tua frase formativa leggermente fuorviante.
Sergiy Kolodyazhnyy,

Risposte:


8

Non puoi. Il modo per ottenere una casecaduta è sostituire il ;;separatore con ;&(o ;;&). Ed è un errore di sintassi per inserirlo in un if.

Potresti scrivere l'intera logica come un normale condizionale:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi

Sì, può ! :)
NotAnUnixNazi

@Isaac, beh, sì, anche se hanno specificato basando il fallthrough su un if.
ilkkachu,

+1 Per essere chiari: ho votato a favore la tua risposta. :).
NotAnUnixNazi

7

Suggerirei di ristrutturare la tua logica: metti invece il codice "fallthrough" in una funzione:

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

uscite

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!

7

Il seguente script trasforma il tuo test "dentro e fuori", nel senso che $vartestiamo prima e poi eseguiamo il fallthrough (usando ;&in a case) a seconda $input.

Lo facciamo perché la questione se "eseguire il fallthrough" dipende davvero solo $inputse lo $varè 1. Se si tratta di un altro valore, non è nemmeno necessario porsi la domanda se effettuare il fallthrough.

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

Oppure, senza case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi

Penso che tu abbia inchiodato ciò che OP voleva davvero, che sembra saltare dalla loro dichiarazione if originale a qualunque cosa dentro *). Ho già il mio +1.
Sergiy Kolodyazhnyy,

5

Non qualcosa che farei, ma potresti ottenere qualcosa che si avvicina 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

2

Prova entrambe le variabili contemporaneamente (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

Sui test:

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

Pulito e semplice.

Comprendi che il valore testato ( $new) deve avere solo due possibili valori, ecco perché la clausola if è presente, per trasformare VAR in un valore booleano. Se VAR può essere impostato per essere un valore booleano, provare per 0(non 1) in casee rimuovere il file if.


1

È possibile impostare il fallthrough come predefinito, ma inserire una condizione che il codice esegue solo se la condizione è soddisfatta

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

Ma come suggerito ilkkachu puoi anche usare le condizioni piuttosto che passare.


1

Se non ti dispiace che qualcuno che si lamenta non capisca il tuo codice, potresti semplicemente cambiare l'ordine dei due condizionali:

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!"

Semplice e chiaro (almeno per me). casenon supporta il fallthrough stesso. Ma puoi sostituirlo *)con &&after esacper renderlo rispettoso dei valori di ritorno di altri rami.


-4

Nuovi ;&e ;;&operatori sono stati introdotti in Bash 4.0 e sebbene entrambi possano essere utili in situazioni simili, penso che non siano utili nel tuo caso. Questo è ciò che man bashdice di questi operatori:

Se la ;; viene utilizzato l'operatore, non viene tentata alcuna corrispondenza successiva dopo la prima corrispondenza del modello. Utilizzando; & al posto di ;; fa sì che l'esecuzione continui con l'elenco associato al set di schemi successivo. Utilizzando ;; & al posto di ;; fa sì che la shell verifichi l'eventuale elenco di schemi successivo nell'istruzione ed esegua qualsiasi elenco associato su una corrispondenza corretta.

In altre parole, ;&è una caduta e come lo sappiamo da Ce ;;&fa bashcontrollare i casi rimanenti invece di tornare completamente dal caseblocco. Puoi trovare un bell'esempio di ;;&in azione qui: /programming//a/24544780/3691891 .

Detto questo, ;&né nessuno dei due ;;&potrebbe essere usato nella tua sceneggiatura perché entrambi andrebbero a *)quello che sarebbe sempre eseguito.

Il seguente script funziona e fa quello che vuoi senza riorganizzare la logica ma consideralo solo come esempio e non fare mai affidamento su di esso, è troppo fragile. Ho preso l'idea da qui :

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

Perché un downvote?
Arkadiusz Drabczyk,

1
Questo potrebbe funzionare in un esempio di giocattolo, ma è quasi incomprensibile e non funzionerebbe in una sceneggiatura più grande. Il modo in cui simuli il goto è estremamente fragile, e dire che simula davvero il goto è un'esagerazione. Il codice ritorna dalla jumptofunzione ed esegue tutto ciò che viene dopo. Il fatto che ciò equivale a continuare l'esecuzione dopo il blocco tra ft:ed ;;è una coincidenza perché jumptoè l'ultimo comando nello stesso comando complesso del blocco saltato.
Gilles 'SO- smetti di essere malvagio'

1
OK, l'ho sottolineato chiaramente nella mia risposta.
Arkadiusz Drabczyk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.