Processo de queda baseado na condição if

10

Estou procurando uma maneira de ter uma queda baseada em uma condição if dentro de uma condição de caso no bash. Por exemplo:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

No código acima, se a variável VAR for 1 , gostaria que a condição do caso fosse executada.

    
por Smashgen 28.05.2018 / 21:07

8 respostas

8

Você não pode. A maneira de obter uma case é substituir o separador ;; por ;& (ou ;;& ). E é um erro de sintaxe para colocar isso dentro de um if.

Você pode escrever toda a lógica como uma condicional regular:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
    
por 28.05.2018 / 22:05
7

Eu sugeriria reestruturar sua lógica: coloque o código "fallthrough" em uma função:

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

saídas

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!
    
por 28.05.2018 / 21:56
7

O script a seguir transforma seu teste "de dentro para fora" no sentido de que testamos $var primeiro e depois realizamos o avanço (usando ;& em case ) dependendo de $input .

Fazemos isso porque a questão de se "executar o avanço" depende apenas de $input if $var is 1 . Se é qualquer outro valor, a questão de se fazer a queda nem precisa ser feita.

#/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, sem case :

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
    
por 28.05.2018 / 22:18
5

Não é algo que eu faria, mas você poderia alcançar algo que se aproximasse com:

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
    
por 28.05.2018 / 23:41
2

Teste ambas as variáveis de uma só 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

No teste:

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

Limpo e simples.

Entenda que o valor testado ( $new ) deve ter apenas dois valores possíveis, é por isso que a cláusula if está lá, para transformar VAR em um valor booleano. Se VAR pode ser feito para ser um booleano, teste 0 (não 1 ) no case e remova o if .

    
por 29.05.2018 / 10:46
1

Você pode tornar a queda padrão, mas colocar uma condição de que o código só é executado apenas se a condição for atendida

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

Mas, como ilkkachu sugeriu, você também pode usar condições em vez de alternar.

    
por 29.05.2018 / 10:02
1

Se você não se incomodar com alguém reclamando que ele não entende seu código, basta alternar a ordem dos dois condicionais:

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

Simples e claro (pelo menos para mim). case não suporta a própria penetração. Mas você pode substituir *) por && após esac para fazer com que ele respeite os valores de retorno de outras ramificações.

    
por 29.05.2018 / 14:38
-4

Os novos operadores ;& e ;;& foram introduzidos em Bash 4,0 e embora ambos possam ser úteis em situações semelhantes, penso eles não são úteis no seu caso. Isto é o que diz man bash sobre esses operadores:

If the ;; operator is used, no subsequent matches are attempted after the first pattern match. Using ;& in place of ;; causes execution to continue with the list associated with the next set of patterns. Using ;;& in place of ;; causes the shell to test the next pattern list in the statement, if any, and execute any associated list on a successful match.

Em outras palavras, ;& é uma queda e como sabemos de C e ;;& faz bash verificar os casos restantes em vez de retornar de case bloqueia completamente. Você pode encontrar um bom exemplo de ;;& em ação aqui: link .

Dito isto, nem ;& nem ;;& pode ser usado no seu script porque ambos iriam para *) que seria sempre executado.

O script a seguir funciona e faz o que você deseja sem reorganizar a lógica, mas considerá-lo apenas como um exemplo e nunca confiar nele, é muito frágil. Eu tirei a ideia de aqui :

#!/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
    
por 28.05.2018 / 22:05