Recherche de cas en fonction de la condition if

11

Je cherche un moyen de faire en sorte que la chute se produise en fonction d'une condition if dans une condition de cas dans bash. Par exemple:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

Dans le code ci-dessus, si la variable VARest 1, je voudrais que la condition de casse soit exécutée.

Smashgen
la source
1
Petite question: essayez-vous de passer d'une if [ $VAR -eq 1 ]; thenpartie du code à ce qu'il contient *)? Parce que c'est complètement différent de ce que l'on appelle la solution, ce qui rend la formulation de votre question légèrement trompeuse.
Sergiy Kolodyazhnyy

Réponses:

8

Tu ne peux pas. La façon d'avoir une casechute est de remplacer le ;;séparateur par ;&(ou ;;&). Et c'est une erreur de syntaxe de mettre cela dans un if.

Vous pouvez écrire toute la logique comme conditionnelle régulière:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
ilkkachu
la source
Oui, il le peut ! :)
Isaac
@Isaac, eh bien, oui, bien qu'ils aient spécifié de baser la chute sur un if.
ilkkachu
+1 Pour être clair: j'ai voté pour votre réponse. :).
Isaac
7

Je suggère de restructurer votre logique: mettez plutôt le code "fallthrough" dans une fonction:

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

les sorties

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!
glenn jackman
la source
7

Le script suivant transforme votre test "à l'envers" dans le sens où nous testons d' $varabord, puis effectuons la solution (en utilisant ;&a case) en fonction de $input.

Nous faisons cela parce que la question de savoir si ou non « effectuer la fallthrough » dépend vraiment seulement $inputsi $varest 1. S'il s'agit d'une autre valeur, il n'est même pas nécessaire de se poser la question de savoir si faire la solution.

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

Ou, sans case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
Kusalananda
la source
Je pense que vous avez cloué ce que OP voulait réellement, ce qui semble sauter de sa déclaration if originale à quoi que ce soit *). J'ai déjà mon +1.
Sergiy Kolodyazhnyy
5

Pas quelque chose que je ferais, mais vous pourriez réaliser quelque chose avec:

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
la source
2

Testez les deux variables à la fois (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

Lors des tests:

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

Propre et simple.

Comprenez que la valeur testée ( $new) ne doit avoir que deux valeurs possibles, c'est pourquoi la clause if est là, pour transformer VAR en valeur booléenne. Si VAR peut devenir un booléen, testez 0(pas 1) dans le caseet supprimez le if.

Isaac
la source
1

Vous pouvez définir la valeur par défaut, mais placer une condition selon laquelle le code ne s'exécute que si la condition est remplie

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

Mais comme ilkkachu l'a suggéré, vous pouvez également utiliser des conditions plutôt que de changer.

yashC
la source
1

Si cela ne vous dérange pas que quelqu'un se plaint de ne pas comprendre votre code, vous pouvez simplement changer l'ordre des deux conditions:

input="foo"
VAR="1"

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

Ou:

input="foo"
VAR="1"

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

Simple et clair (du moins pour moi). casene prend pas en charge la solution elle-même. Mais vous pouvez remplacer *)par &&after esacpour le faire respecter les valeurs de retour des autres branches.

user23013
la source
-4

De nouveaux opérateurs ;&et ;;&ont été introduits dans la version Bash 4.0 et bien qu'ils puissent tous deux être utiles dans des situations similaires, je pense qu'ils ne sont d'aucune utilité dans votre cas. Voici ce que man bashdisent ces opérateurs:

Si la ;; est utilisé, aucune correspondance ultérieure n'est tentée après la première correspondance de modèle. Utiliser; & au lieu de ;; entraîne l'exécution de la liste associée au prochain ensemble de modèles. Utilisation de ;; & à la place de ;; oblige le shell à tester la liste de modèles suivante dans l'instruction, le cas échéant, et à exécuter toute liste associée sur une correspondance réussie.

En d' autres termes, ;&est une chute à travers et comme nous le savons à partir Cet ;;&marques bashvérifier cas restants au lieu de retourner du casebloc entièrement. Vous pouvez trouver un bel exemple de ;;&en action ici: /programming//a/24544780/3691891 .

Cela étant dit, ni l'un ;&ni l' autre ne ;;&peuvent être utilisés dans votre script car les deux y iraient et *)seraient toujours exécutés.

Le script suivant fonctionne et fait ce que vous voulez sans réorganiser la logique, mais ne le considérez que comme un exemple et ne vous y fiez jamais, il est trop fragile. J'ai pris l'idée d' ici :

#!/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
la source
Pourquoi un downvote?
Arkadiusz Drabczyk
1
Cela peut fonctionner dans un exemple de jouet, mais c'est presque incompréhensible et ne fonctionnerait pas dans un script plus grand. La façon dont vous simulez le goto est extrêmement fragile, et dire qu'il simule vraiment le goto est une exagération. Le code revient de la jumptofonction et exécute tout ce qui vient après. Le fait que cela équivaut à poursuivre l'exécution après le bloc entre ft:et ;;est une coïncidence car jumptoc'est la dernière commande de la même commande complexe que le bloc sauté.
Gilles 'SO- arrête d'être méchant'
1
D'accord, j'ai clairement souligné cela dans ma réponse.
Arkadiusz Drabczyk