Происхождение случая в зависимости от условия

11

Я ищу способ, чтобы произошел кризис, основанный на условии if в условии case в bash. Например:

input="foo"
VAR="1"

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

        # perform fallthrough

    else

        # do not perform fallthrough

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

В приведенном выше коде, если переменная VARесть 1, я бы хотел, чтобы условие case выполняло падение.

Smashgen
источник
1
Небольшой вопрос: вы пытаетесь перейти от if [ $VAR -eq 1 ]; thenчасти кода к тому, что находится внутри *)? Потому что это полностью отличается от того, что называется «прорыв», что делает ваш вопрос формулировочным, слегка вводящим в заблуждение.
Сергей Колодяжный

Ответы:

8

Ты не можешь Способ caseпровалиться - заменить ;;разделитель на ;&(или ;;&). И это синтаксическая ошибка, чтобы поместить это в if.

Вы можете написать всю логику как обычное условие:

if [ "$input" != "foo" ] || [ "$VAR" = 1 ]; then
    one branch ...
else   # $input = "foo" && $VAR != 1
    another branch...
fi
ilkkachu
источник
Да, он может ! :)
NotAnUnixNazi
@ Исаак, ну, да, хотя они и указали, что это основано на анализе if.
ilkkachu
+1 Для ясности: я проголосовал за ваш ответ. :).
NotAnUnixNazi
7

Я бы предложил реструктурировать вашу логику: вместо этого поместите «сквозной» код в функцию:

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

выходы

foo 1
falling through
fallthrough worked!
foo 2
not falling through
bar 1
fallthrough worked!
bar 2
fallthrough worked!
Гленн Джекман
источник
7

Следующий скрипт выворачивает ваш тест "наизнанку" в том смысле, что мы $varсначала тестируем, а затем выполняем анализ (используя ;&в a case) в зависимости от $input.

Мы делаем это потому, что вопрос о том, стоит ли «выполнять прорыв», действительно зависит только от « $inputесли $varесть» 1. Если это какая-то другая ценность, вопрос о том, стоит ли делать прорыв, даже не нужно задавать.

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

Или без case:

if [ "$var" -eq 1 ]; then
    if [ "$input" = 'foo' ]; then
        echo 'perform fallthrough'
    fi
    echo 'fallthough worked'
else
    echo 'what fallthrough?'
fi
Кусалананда
источник
Я думаю, что вы добились того, чего на самом деле хотел ОП, который, кажется, прыгает от своего первоначального утверждения к чему-либо еще *). Уже есть мой +1.
Сергей Колодяжный
5

Не то, что я бы сделал, но вы могли бы достичь чего-то, связанного с:

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

Проверьте обе переменные одновременно (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

На тестировании:

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

Чисто и просто.

Поймите, что проверяемое значение ( $new) должно иметь только два возможных значения, поэтому есть предложение if, чтобы преобразовать VAR в логическое значение. Если VAR может быть задан как логическое значение, то проверьте 0(не 1) в caseи удалите if.

NotAnUnixNazi
источник
1

Вы можете сделать сквозной режим по умолчанию, но поставить условие, что код будет выполняться только в том случае, если условие выполнено

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

Но, как предложил ilkkachu, вы также можете использовать условия, а не переключаться.

yashC
источник
1

Если вы не возражаете против того, чтобы кто-то жаловался на то, что он не понимает ваш код, вы можете просто изменить порядок двух условий:

input="foo"
VAR="1"

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

Или:

input="foo"
VAR="1"

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

Просто и понятно (по крайней мере для меня). caseне поддерживает само падение. Но вы можете заменить *)на &&after, esacчтобы относиться к возвращаемым значениям других ветвей.

user23013
источник
-4

Новые операторы ;&и ;;&операторы были введены в Bash 4.0, и хотя они оба могут быть полезны в аналогичных ситуациях, я думаю, что они бесполезны в вашем случае. Вот что man bashговорит об этих операторах:

Если ;; оператор используется, никакие последующие совпадения не предпринимаются после первого сопоставления с образцом. Использование; & вместо ;; заставляет выполнение продолжить со списком, связанным со следующим набором шаблонов. Использование ;; & вместо ;; заставляет оболочку проверить следующий список шаблонов в инструкции, если таковые имеются, и выполнить любой связанный список при успешном совпадении.

Другими словами, ;&это падение до конца и , как мы знаем из Cи ;;&марки bashпроверить остальных случаях вместо возвращения из caseблока целиком. Вы можете найти хороший пример ;;&в действии здесь: /programming//a/24544780/3691891 .

Это, как говорится, ни то, ;&ни другое не ;;&может быть использовано в вашем сценарии, потому что оба они пойдут на *)то, что всегда будет выполняться.

Следующий скрипт работает и делает то, что вы хотите, не перестраивая логику, но рассматривайте это только как пример и никогда не полагайтесь на него, он слишком хрупок. Я взял идею отсюда :

#!/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
Аркадиуш Драбчик
источник
Почему отрицательный голос?
Аркадиуш Драбчик
1
Это может работать в игрушечном примере, но это почти непостижимо и не будет работать в более широком сценарии. То, как вы моделируете goto, чрезвычайно хрупко, и сказать, что оно действительно имитирует goto, - это преувеличение. Код возвращается из jumptoфункции и выполняет все, что следует после нее. Тот факт, что это эквивалентно продолжению выполнения после блока между ft:и ;;является совпадением, поскольку jumptoявляется последней командой в той же сложной команде, что и блок, к которому был выполнен переход.
Жиль "ТАК - перестань быть злым"
1
Хорошо, я четко подчеркнул это в своем ответе.
Аркадиуш Драбчик