การใช้ตัวดำเนินการไม่เท่ากันสำหรับการเปรียบเทียบสตริง


117

ฉันพยายามตรวจสอบว่าPHONE_TYPEตัวแปรมีหนึ่งในสามค่าที่ถูกต้อง

if [ "$PHONE_TYPE" != "NORTEL" ] || [ "$PHONE_TYPE" != "NEC" ] ||
   [ "$PHONE_TYPE" != "CISCO" ]
then
    echo "Phone type must be nortel,cisco or nec"
    exit
fi

รหัสด้านบนใช้งานไม่ได้สำหรับฉันดังนั้นฉันจึงลองทำสิ่งนี้แทน:

if [ "$PHONE_TYPE" == "NORTEL" ] || [ "$PHONE_TYPE" == "NEC" ] ||
   [ "$PHONE_TYPE" == "CISCO" ]
then
    :        # do nothing
else
    echo "Phone type must be nortel,cisco or nec"
    exit
fi

มีวิธีที่สะอาดกว่าสำหรับงานประเภทนี้หรือไม่?

คำตอบ:


162

ฉันเดาว่าคุณกำลังมองหา:

if [ "$PHONE_TYPE" != "NORTEL" ] && [ "$PHONE_TYPE" != "NEC" ] &&
   [ "$PHONE_TYPE" != "CISCO" ]

กฎสำหรับการเทียบเท่าเหล่านี้เรียกว่ากฎหมายของ De Morganและในกรณีของคุณหมายถึง:

not(A || B || C) => not(A) && not(B) && not (C)

บันทึกการเปลี่ยนแปลงในตัวดำเนินการบูลีนหรือและและ

ในขณะที่คุณพยายามทำ:

not(A || B || C) => not(A) || not(B) || not(C)

เห็นได้ชัดว่าไม่ได้ผล


28

วิธีที่สั้นกว่านั้นคือ:

if [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then 
  echo "Phone type must be nortel, cisco or nec."
fi
  • ^ - เพื่อให้ตรงกับการเริ่มต้นที่จุดเริ่มต้นของบรรทัด
  • $ - เพื่อให้ตรงกับจุดสิ้นสุดของบรรทัด
  • =~ - โอเปอเรเตอร์การเปรียบเทียบนิพจน์ทั่วไปในตัวของ Bash

2
ฉันคิดว่าน่าจะเป็นif [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then
Milan Simek

12

คำตอบที่ดีและบทเรียนอันล้ำค่า;) ต้องการเสริมด้วยบันทึกย่อเท่านั้น

การทดสอบประเภทใดที่เลือกใช้ขึ้นอยู่กับรหัสโครงสร้างสภาพแวดล้อมและอื่น ๆ

ทางเลือกอื่นอาจใช้สวิตช์หรือcaseคำสั่งดังใน:

case "$PHONE_TYPE" in
"NORTEL"|"NEC"|"CISCO")
    echo "OK"
    ;;
*)
    echo "Phone type must be nortel,cisco or nec"
    ;;
esac

ในฐานะโน้ตที่สองคุณควรระวังโดยใช้ชื่อตัวแปรตัวพิมพ์ใหญ่ เพื่อป้องกันการชนกันระหว่างตัวแปรที่ระบบแนะนำซึ่งเกือบทุกกรณีเป็นตัวพิมพ์ใหญ่ ดังนั้นแทนที่จะ$phone_type$PHONE_TYPE

แม้ว่าคนนั้นจะปลอดภัย แต่ถ้าคุณมีนิสัยที่ใช้ตัวพิมพ์ใหญ่ทั้งหมด แต่วันหนึ่งคุณอาจพูดIFS="boo"และคุณอยู่ในโลกที่เจ็บปวด

นอกจากนี้ยังจะทำให้ง่ายต่อการตรวจสอบซึ่งเป็นสิ่งที่

ไม่จำเป็นต้องมีแต่จะพิจารณาอย่างยิ่ง


มันก็น่าจะเป็นผู้สมัครที่ดีสำหรับฟังก์ชั่น ส่วนใหญ่ทำให้การอ่านและบำรุงรักษารหัสง่ายขึ้น เช่น:

valid_phone_type()
{
    case "$1" in
    "NORTEL"|"NEC")
        return 0;;
    *)
        echo "Model $1 is not supported"
        return 1;;
    esac
}

if ! valid_phone_type "$phone_type"; then
    echo "Bye."
    exit 1
fi

9

คุณควรใช้ ANDs ไม่ใช่ ORs

if [ "$PHONE_TYPE" != "NORTEL" ] && [ "$PHONE_TYPE" != "NEC" ] && [ "$PHONE_TYPE" != "CISCO" ]
then

หรือ

if [ "$PHONE_TYPE" != "NORTEL" -a "$PHONE_TYPE" != "NEC" -a "$PHONE_TYPE" != "CISCO" ]
then

1

หากต้องการแก้ไขคำตอบข้างต้น (เนื่องจากฉันยังไม่สามารถแสดงความคิดเห็นได้):

PHONE_TYPE="NORTEL"
if [[ $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO|SPACE TEL)$ ]]; then 
  echo "Phone type accepted."
else
  echo "Error! Phone type must be NORTEL, CISCO or NEC."
fi

โปรดทราบว่าคุณต้องการอย่างน้อยทุบตี 4 สำหรับการใช้ =
นี้มันไม่ได้ทำงานในทุบตี 3

ฉันทดสอบกับ MS Windows 7 โดยใช้ bash 4.3.46 (ใช้งานได้ดี) และ bash 3.1.17 (ไม่ทำงาน)

LHS ของ = ~ ควรอยู่ในเครื่องหมายคำพูด ด้านบน PHONE_TYPE = "SPACE TEL" จะตรงกันเช่นกัน


0

ใช้ [[แทน

if [[ "$PHONE_TYPE" != "NORTEL" ]] || [[ "$PHONE_TYPE" != "NEC" ]] || 
   [[ "$PHONE_TYPE" != "CISCO" ]]
then
echo "Phone type must be nortel,cisco or nec"
exit 1
fi

2
แน่นอนว่านี่ผิด [[vs [ไม่ช่วยด้วยตรรกะที่ถูกปิด
ilkkachu

0

ข้อเสนอรูปแบบที่หลากหลายขึ้นอยู่กับโซลูชัน @ 0x80:

# define phone brand list
phoneBrandList=" NORTEL NEC CISCO" ## separator is space with an extra space in first place

# test if user given phone is contained in the list
if [[ ${phoneBrandList} =~ (^|[[:space:]])"${userPhoneBrand}"($|[[:space:]]) ]]; then
    echo "found it !"
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.