ตัวดำเนินการเชิงตรรกะหลายตัว ((A || B) && C) และ“ ข้อผิดพลาดทางไวยากรณ์ใกล้โทเค็นที่ไม่คาดคิด”


24

ฉันทำงานกับ Bash 3 และฉันพยายามสร้างเงื่อนไข ใน C / C ++ มันง่ายที่ตายแล้ว: ((A || B) && C). ใน Bash มันไม่เป็นเช่นนั้น (ฉันคิดว่าผู้แต่ง Git ต้องมีส่วนร่วมในรหัสนี้ก่อนที่จะย้ายไปสู่ความพยายามอื่น ๆ )

สิ่งนี้ใช้ไม่ได้ โปรดทราบว่า<0 or 1>ไม่ใช่ตัวอักษรสตริง; มันหมายถึง 0 หรือ 1 (โดยทั่วไปมาจากgrep -i)

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

มันส่งผลใน:

line 322: syntax error near unexpected token `[['

ฉันลองแล้ว:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

มันส่งผลให้:

line 322: syntax error near unexpected token `[['

ส่วนหนึ่งของปัญหาคือผลการค้นหาเป็นตัวอย่างเล็ก ๆ น้อย ๆ และไม่ใช่ตัวอย่างที่ซับซ้อนกว่าที่มีเงื่อนไขแบบผสม

ฉันจะแสดงแบบเรียบง่าย((A || B) && C)ใน Bash ได้อย่างไร


ฉันพร้อมที่จะคลี่คลายมันและทำซ้ำคำสั่งเดียวกันในหลาย ๆ บล็อก:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

คำตอบ:


42

ไวยากรณ์ของ bash ไม่เหมือนกับ C แม้ว่าส่วนเล็ก ๆ ของมันจะได้รับแรงบันดาลใจจาก C คุณไม่สามารถลองเขียนโค้ด C และคาดหวังให้มันทำงานได้

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

ดังนั้นเมื่อคุณเขียน

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

ที่แยกเป็น

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

ซึ่งเหมือนกับ

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

สังเกตเห็นวงเล็บเหลี่ยมไม่สมดุล? ใช่มันไม่ดี ความพยายามของคุณในวงเล็บมีปัญหาเดียวกัน: วงเล็บปลอม

ไวยากรณ์ของคำสั่งกลุ่มรวมกันเป็นวงเล็บปีกกา วิธีการแยกวิเคราะห์วงเล็บต้องใช้คำสั่งที่สมบูรณ์ก่อนที่พวกเขาดังนั้นคุณจะต้องยุติคำสั่งภายในวงเล็บด้วย newline หรืออัฒภาค

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

มีทางเลือกอื่นคือการใช้วงเล็บคู่ ซึ่งแตกต่างจากวงเล็บเดี่ยว, วงเล็บคู่เป็นไวยากรณ์เปลือกพิเศษ พวกเขากำหนดเขตการแสดงออกเงื่อนไข ในวงเล็บคู่คุณสามารถใช้วงเล็บและผู้ประกอบการเช่นและ&& ||เนื่องจากเครื่องหมายวงเล็บคู่เป็นไวยากรณ์เชลล์เชลล์จึงรู้ว่าเมื่อตัวดำเนินการเหล่านี้อยู่ในวงเล็บพวกเขาจะเป็นส่วนหนึ่งของไวยากรณ์นิพจน์ที่มีเงื่อนไขไม่ใช่ส่วนหนึ่งของไวยากรณ์คำสั่งเชลล์ทั่วไป

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

หากทุกอย่างของการทดสอบของคุณมีตัวเลขมีวิธีการอื่น ๆ ซึ่งกำหนดเขตการแสดงออก artihmetic นิพจน์ทางคณิตศาสตร์ดำเนินการคำนวณจำนวนเต็มด้วยไวยากรณ์ C-like มาก

if (((A == 0 || B != 0) && C == 0)); then 

คุณอาจพบว่าไพรเมอร์วงเล็บ bash ของฉันมีประโยชน์

[สามารถใช้ใน SH ธรรมดา [[และ((เฉพาะสำหรับ bash (และ ksh และ zsh)

¹ นอกจากนี้ยังสามารถรวมการทดสอบหลายรายการเข้ากับโอเปอเรเตอร์บูลีนได้ แต่การใช้งานลำบากและมีข้อผิดพลาดเล็กน้อยดังนั้นฉันจะไม่อธิบาย


ขอบคุณไจล์ส สิ่งนี้มีไว้สำหรับ Bash 2 ถึง Bash 4 หรือไม่? ฉันต้องการสิ่งอย่างอ่อนโยนพกพา แต่ Kusalananda กล่าวถึงบางสิ่งบางอย่างผมก็ไม่ได้ทำแบบพกพา (ไม่ที่บ่งบอกถึงสิ่งที่ผมกำลังทำอยู่ไม่ได้พกพา?) ที่นี่อย่างอ่อนโยนหมายถึง Bash 2 ถึง Bash 4

@jww [[ … ]]ถูกเพิ่มใน bash 2.02 ((…))ถูกเพิ่มใน bash 2.0
Gilles 'หยุดความชั่วร้าย'

@Giles - ขอบคุณ Bash 2 น่าจะเป็นเรื่องที่น่าหัวเราะสำหรับทุกคน มันเป็นเพราะการกำกับดูแลของเราและไม่เต็มใจที่จะละทิ้งแพลตฟอร์มเก่า Bash 2 และ GCC 3 แสดงในการทดสอบ Fedora 1 ของเรา เราจะปล่อยให้แพลตฟอร์มเก่าดำเนินต่อไปในบางโอกาส แต่ก็ต้องมีเหตุผลด้วย เราไม่สมัครเป็นสมาชิกของ Apple, Microsoft, Mozilla, etc.

@jww โปรดเข้าใจว่าความสำคัญของ||และ&&เปลี่ยนจาก[การและ[[ ลำดับความสำคัญ((เท่ากันใน(ใช้วงเล็บเพื่อควบคุมลำดับการประเมินผล) แต่ && มีลำดับความสำคัญสูงกว่าในและมากกว่า(ตามใน C) [[[((||


3

ใช้-oโอเปอเรเตอร์แทนการใช้ | | ซ้อนกัน คุณสามารถใช้-aเพื่อแทนที่ && หากจำเป็นในงบอื่น ๆ ของคุณ

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi

ขอบคุณ ฉันเจอ-aแล้วและ-oแต่ฉันไม่ได้ทดลองกับพวกเขา มีคนใน Stack Overflow กล่าวเพื่อหลีกเลี่ยง ฉันเห็นด้วยกับพวกเขาเป็นส่วนใหญ่เนื่องจากสคริปต์ไม่สามารถอ่านได้โดยใช้พวกเขา

... แต่พกพาได้มากกว่า
Kusalananda

@ Kusalananda - ขอขอบคุณสำหรับความสำเร็จในการพกพา สคริปต์จะต้องทำงานในระบบที่มี Bash 2 ถึง Bash 4 จะ[[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]มีปัญหาหรือไม่

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