Випадок випадків виходячи з умови наявності


11

Я шукаю спосіб, як це відбудеться, виходячи з умови if в умові випадку в 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, я б хотів, щоб умова справи виконувалась внизу.


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

Так, він може ! :)
Ісаак

@Isaac, добре, так, хоча вони вказали, грунтуючись на прориві на if.
ilkkachu

+1 Щоб було зрозуміло: я проголосував за вашу відповідь. :).
Ісаак

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спочатку, а потім виконуємо перехід (використовуючи ;&а 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.


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, ви також можете використовувати умови, а не перемикатися.


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щоб це було відносно повернених значень інших гілок.


-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
Гаразд, я чітко підкреслив це у своїй відповіді.
Аркадіуш Драбчик
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.