ข้อผิดพลาดกรณีขึ้นอยู่กับเงื่อนไขถ้า


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ฉันต้องการให้เงื่อนไขกรณีดำเนินการผิดพลาด


1
คำถามเล็ก ๆ : คุณพยายามที่จะข้ามจากif [ $VAR -eq 1 ]; thenส่วนหนึ่งของรหัสไปยังสิ่งที่อยู่ใน*)? เพราะมันแตกต่างอย่างสิ้นเชิงกับสิ่งที่เรียกว่าการตกหล่นดังนั้นการสร้างประโยคคำถามของคุณจะทำให้เข้าใจผิดเล็กน้อย
Sergiy Kolodyazhnyy

คำตอบ:


8

คุณทำไม่ได้ วิธีที่จะทำให้การcaseล้มคือการแทนที่;;ตัวคั่นด้วย;&(หรือ;;&) และเป็นข้อผิดพลาดทางไวยากรณ์ที่จะนำสิ่งนั้นมาใส่ใน if

คุณสามารถเขียนตรรกะทั้งหมดออกเป็นเงื่อนไขปกติ:

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


@Isaac ดีใช่แม้ว่าพวกเขาจะระบุ basing fallthrough ifบน
ilkkachu

+1 อย่างชัดเจน: ฉันโหวตให้คำตอบแล้ว :)
ไอแซ

7

ฉันขอแนะนำให้ปรับโครงสร้างตรรกะของคุณ: วางโค้ด "fallthrough" ลงในฟังก์ชั่นแทน:

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ครั้งแรกและจากนั้นทำการ fallthrough (ใช้;&ใน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

ฉันคิดว่าคุณตอกสิ่ง OP *)ต้องการจริงซึ่งดูเหมือนว่าจะกระโดดจากเดิมของพวกเขาถ้ามีคำสั่งอะไรก็ตามที่อยู่ใน มี +1 ของฉันอยู่แล้ว
Sergiy Kolodyazhnyy

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) จะต้องมีค่าที่เป็นไปได้เพียงสองค่านั่นคือสาเหตุที่ข้อถ้าหากมีเพื่อเปลี่ยน VAR เป็นค่าบูลีน หาก VAR อาจจะทำให้เป็นแบบบูลแล้วสำหรับการทดสอบ0(ไม่1) ในและลบcaseif


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ไม่รองรับข้อผิดพลาดเอง แต่คุณสามารถแทนที่*)ด้วย&&หลังจาก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

ทำไมต้องลงคะแนน
Arkadiusz Drabczyk

1
สิ่งนี้อาจทำงานในตัวอย่างของเล่น แต่ไม่สามารถเข้าใจได้และจะไม่ทำงานในบทที่ใหญ่กว่า วิธีที่คุณจำลองการข้ามไปนั้นมีความเปราะบางอย่างยิ่งและการบอกว่าการเลียนแบบการข้ามไปนั้นเป็นการพูดเกินจริง โค้ดส่งคืนจากjumptoฟังก์ชันและดำเนินการสิ่งที่เกิดขึ้นหลังจากนั้น ความจริงที่ว่านี้เทียบเท่ากับการดำเนินการต่อหลังจากบล็อกระหว่างft:และ;;เป็นเรื่องบังเอิญเพราะjumptoเป็นคำสั่งสุดท้ายในคำสั่งที่ซับซ้อนเช่นเดียวกับการข้ามไปยังบล็อก
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

1
ตกลงฉันเน้นอย่างชัดเจนว่าในคำตอบของฉัน
Arkadiusz Drabczyk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.