วิธีการแสดงหลายเงื่อนไขในเปลือกถ้าคำสั่ง?


308

ฉันต้องการเป็นตัวแทนของหลายเงื่อนไขเช่นนี้:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

แต่เมื่อฉันรันสคริปต์มันจะแสดงขึ้น

syntax error at line 15: `[' unexpected, 

เมื่อบรรทัดที่ 15 เป็นบรรทัดที่แสดงว่า ....

เกิดอะไรขึ้นกับเงื่อนไขนี้ ()ผมคิดว่าสิ่งที่เป็นธรรมกับ


6
คุณไม่ได้ถามเกี่ยวกับเงื่อนไขของเชลล์ แต่เป็นเงื่อนไขการทดสอบ นิพจน์ทั้งหมดในตัวอย่างของคุณถูกประเมินโดยtest( [) และไม่ใช่โดยเชลล์ [เปลือกประเมินเพียงแค่ออกจากสถานะของ
เซเว่น


คำตอบ:


381

เทคนิคคลาสสิก (Escape metacharacters):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

ฉันได้ใส่การอ้างอิงไว้$gในเครื่องหมายคำพูดคู่ เป็นการปฏิบัติที่ดีโดยทั่วไป อย่างเคร่งครัดวงเล็บไม่จำเป็นเพราะความสำคัญของ-aและ-oทำให้มันถูกต้องแม้ไม่มีพวกเขา

หมายเหตุว่า-aและ-oผู้ประกอบการเป็นส่วนหนึ่งของข้อกำหนด POSIX สำหรับtestอาคา[ส่วนใหญ่สำหรับการทำงานร่วมกันหลัง (ตั้งแต่พวกเขาเป็นส่วนหนึ่งของtestในรุ่นที่ 7 UNIX เป็นต้น) แต่พวกเขามีการทำเครื่องหมายอย่างชัดเจนว่า 'หมดโดย POSIX Bash (ดูนิพจน์แบบมีเงื่อนไข ) ดูเหมือนว่าจะ preempt ความหมายแบบคลาสสิคและ POSIX สำหรับ-aและ-oด้วยตัวดำเนินการทางเลือกของตัวเองที่รับอาร์กิวเมนต์


ด้วยความระมัดระวังคุณสามารถใช้[[โอเปอเรเตอร์ที่ทันสมัยกว่าได้ แต่ระวังว่าเวอร์ชั่นใน Bash และ Korn Shell (ตัวอย่าง) ไม่จำเป็นต้องเหมือนกัน

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

ตัวอย่างการรันโดยใช้ Bash 3.2.57 บน Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

คุณไม่จำเป็นต้องพูดตัวแปรในแบบ[[เดียวกับที่คุณทำ[เพราะมันไม่ใช่คำสั่งที่แยกกันในแบบเดียวกับที่[เป็น


นี่ไม่ใช่คำถามคลาสสิกใช่ไหม

ฉันจะมีความคิดเช่นนั้น. อย่างไรก็ตามมีทางเลือกอื่นคือ:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

แน่นอนถ้าคุณอ่านคำแนะนำ 'พกพาเชลล์' สำหรับautoconfเครื่องมือหรือแพ็คเกจที่เกี่ยวข้องสัญลักษณ์นี้ - โดยใช้ ' ||' และ ' &&' - เป็นสิ่งที่พวกเขาแนะนำ ฉันคิดว่าคุณสามารถไปได้ไกลถึง:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

ในกรณีที่การกระทำนั้นไม่สำคัญเท่ากับการสะท้อนสิ่งนี้ก็ไม่เลว เมื่อบล็อกการดำเนินการที่ต้องทำซ้ำเป็นหลายบรรทัดการทำซ้ำนั้นเจ็บปวดเกินไปและหนึ่งในเวอร์ชันก่อนหน้านั้นเป็นที่นิยมมากกว่าหรือคุณต้องใส่การกระทำลงในฟังก์ชันที่เรียกใช้ในthenบล็อกที่แตกต่างกัน


9
เป็นการดีที่จะรู้ว่าการทำงานของวงเล็บที่หนี เช่นกันในกรณีนี้โดยเฉพาะอย่างยิ่งวงเล็บไม่จำเป็นแม้เพราะ-aจริงมีลำดับความสำคัญสูงกว่า-o(เหมือน&&และ||ในเปลือก - แต่ภายใน bash [[ ... ]] เงื่อนไข , && นอกจากนี้ยังมีความสำคัญสูงกว่า||) หากคุณต้องการหลีกเลี่ยง-aและ-oเพื่อความทนทานและพกพาได้ดีที่สุดซึ่งPOSIX man pageแนะนำเอง - คุณสามารถใช้subshellsสำหรับการจัดกลุ่ม:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0

วิธีแก้ปัญหาที่ดี แต่ฉันชอบรูปแบบที่ไม่มีวงเล็บและวงเล็บทั้งหมด:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK

ฉันช้าไปนิดหน่อย แต่มันก็ยังเป็นผลการค้นหาอันดับต้น ๆ ดังนั้นฉันแค่อยากจะทราบว่าการใช้&&หรือ||เป็นที่นิยมมากกว่าเพราะมันทำงานเหมือนกับเงื่อนไขในภาษาอื่น ๆ และช่วยให้คุณลัดวงจรเงื่อนไข ตัวอย่างเช่นif [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. การทำเช่นนี้ถ้าcheck_inodesว่างคุณหลีกเลี่ยงการเรียกสองstatครั้งในขณะที่เงื่อนไขการทดสอบที่ใหญ่และซับซ้อนจะต้องประมวลผลอาร์กิวเมนต์ทั้งหมดก่อนที่จะดำเนินการ (ซึ่งอาจนำไปสู่ข้อบกพร่องหากคุณลืมพฤติกรรมนั้น)
Haravikk

182

ใน Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

9
bashแน่นอนวิธีที่ดีที่สุดใน เช่นกันในกรณีนี้โดยเฉพาะอย่างยิ่งวงเล็บไม่จำเป็นแม้เพราะภายใน [[ ... ]] เงื่อนไข &&จริงมีลำดับความสำคัญสูงกว่า||- แตกต่างนอกเงื่อนไขดังกล่าว
mklement0

มีวิธีใดบ้างที่เราจะทราบว่าเงื่อนไขตรงกับที่นี่หรือไม่? มันคือหนึ่งในวงเล็บแรกหรืออีกอันหนึ่ง?
Firelord

1
@ Firelord: คุณจะต้องแยกเงื่อนไขออกเป็นif/ elsestatement และมีรหัสระหว่างthenและfiใส่ในฟังก์ชั่นเพื่อหลีกเลี่ยงการทำซ้ำ ในกรณีที่ง่ายมากคุณสามารถใช้caseคำสั่งที่มีการ;&ตกหล่น (ใน Bash 4)
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
จุดประสงค์ของวงเล็บสองเหลี่ยมคืออะไร
peterchaula


37

การใช้/bin/bashสิ่งต่อไปนี้จะได้ผล:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

ระวังถ้าคุณมีช่องว่างในตัวแปรสตริงและตรวจสอบการมีอยู่ ให้แน่ใจว่าได้พูดอย่างถูกต้อง

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

1
เมื่อเราใช้เครื่องหมายปีกกาหลังสัญลักษณ์ดอลลาร์ !!
YouAreAwesome

6
$ g=3
$ c=133
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
efg
$ g=1
$ c=123
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
abc

2
นั่นคือ subshell ที่ไร้ประโยชน์ { ;}สามารถใช้แทน
phk

2

ใน bash สำหรับการเปรียบเทียบสตริงคุณสามารถใช้เทคนิคต่อไปนี้

if [ $var OP "val" ]; then
    echo "statements"
fi

ตัวอย่าง:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi

0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi

3
ยินดีต้อนรับสู่ Stack Overflow! คำถามนี้กำลังมองหาคำอธิบายไม่ใช่เพียงแค่รหัสการทำงาน คำตอบของคุณไม่มีข้อมูลเชิงลึกสำหรับผู้ถามและอาจถูกลบ โปรดแก้ไขเพื่ออธิบายสิ่งที่ทำให้เกิดอาการที่สังเกต
Toby Speight

0

คุณสามารถเชื่อมโยงมากกว่า 2 เงื่อนไขด้วยกัน:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



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