ตัวดำเนินการเชิงตรรกะอย่างง่ายใน Bash


256

ฉันมีตัวแปรสองสามตัวและฉันต้องการตรวจสอบเงื่อนไขต่อไปนี้ (เขียนออกมาเป็นคำพูดจากนั้นฉันก็พยายามล้มเหลวในการทำสคริปต์ทุบตี):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

และในความพยายามที่ล้มเหลวของฉันฉันมาด้วย:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi

คำตอบ:


682

สิ่งที่คุณเขียนแล้วใช้งานได้จริง (มันจะใช้ได้ถ้าตัวแปรทั้งหมดเป็นตัวเลข) แต่มันก็ไม่ได้เป็นวิธีที่งี่เง่าเลย

  • (…)วงเล็บบ่งชี้subshell สิ่งที่อยู่ข้างในนั้นไม่ได้แสดงออกในภาษาอื่น ๆ อีกมากมาย มันเป็นรายการคำสั่ง (เช่นเดียวกับวงเล็บนอก) คำสั่งเหล่านี้จะถูกดำเนินการในกระบวนการย่อยที่แยกต่างหากดังนั้นการเปลี่ยนเส้นทางการกำหนด ฯลฯ ที่ดำเนินการภายในวงเล็บจะไม่มีผลใด ๆ นอกวงเล็บ
    • ด้วยเครื่องหมายดอลลาร์ชั้นนำ$(…)คือการแทนที่คำสั่ง : มีคำสั่งอยู่ในวงเล็บและใช้เอาต์พุตจากคำสั่งเป็นส่วนหนึ่งของบรรทัดคำสั่ง (หลังจากการขยายเพิ่มเติมยกเว้นการทดแทนอยู่ระหว่างเครื่องหมายคำพูดคู่ แต่นั่นเป็นอีกเรื่องหนึ่ง ) .
  • { … }การจัดฟันเป็นเหมือนเครื่องหมายวงเล็บในการที่พวกเขาจัดกลุ่มคำสั่ง แต่พวกเขามีผลเฉพาะกับการแยกวิเคราะห์ไม่ใช่การจัดกลุ่ม โปรแกรมx=2; { x=4; }; echo $xพิมพ์ 4 ในขณะที่x=2; (x=4); echo $xพิมพ์ 2 (วงเล็บปีกกาต้องใช้ช่องว่างรอบ ๆ พวกเขาและเครื่องหมายอัฒภาคก่อนที่จะปิดในขณะที่วงเล็บไม่. นั่นเป็นเพียงการเล่นโวหารไวยากรณ์)
    • ด้วยเครื่องหมายดอลลาร์ชั้นนำ${VAR}คือการขยายพารามิเตอร์ขยายไปถึงมูลค่าของตัวแปรที่มีการเปลี่ยนแปลงเพิ่มเติมที่เป็นไปได้
  • ((…))วงเล็บคู่ล้อมรอบคำสั่งทางคณิตศาสตร์นั่นคือการคำนวณบนจำนวนเต็มด้วยไวยากรณ์ที่คล้ายกับภาษาโปรแกรมอื่น ๆ ไวยากรณ์นี้ส่วนใหญ่จะใช้สำหรับการกำหนดและในเงื่อนไข
    • ไวยากรณ์เดียวกันนี้ใช้ในนิพจน์ทางคณิตศาสตร์$((…))ซึ่งขยายเป็นค่าจำนวนเต็มของนิพจน์
  • [[ … ]]วงเล็บคู่ล้อมรอบนิพจน์เงื่อนไข นิพจน์ตามเงื่อนไขส่วนใหญ่จะสร้างจากตัวดำเนินการเช่น-n $variableเพื่อทดสอบว่าตัวแปรว่างเปล่าหรือไม่และ-e $fileเพื่อทดสอบว่ามีไฟล์อยู่หรือไม่ นอกจากนี้ยังมีผู้ประกอบการสตริงเท่าเทียมกัน: "$string1" == "$string2"(ระวังที่ด้านขวามือเป็นรูปแบบเช่น[[ $foo == a* ]]การทดสอบถ้า$fooเริ่มต้นด้วยaในขณะที่[[ $foo == "a*" ]]การทดสอบถ้า$fooเป็นตรงa*) และคุ้นเคย!, &&และ||ผู้ประกอบการสำหรับการปฏิเสธร่วมและความร้าวฉานเช่นเดียวกับวงเล็บในการจัดกลุ่ม โปรดทราบว่าคุณต้องการช่องว่างรอบตัวดำเนินการแต่ละตัว (เช่น[[ "$x" == "$y" ]]ไม่ใช่[[ "$x"=="$y" ]]) และช่องว่างหรือตัวอักษรที่เหมือน;ทั้งภายในและภายนอกวงเล็บ (เช่น[[ -n $foo ]]ไม่ใช่[[-n $foo]])
  • [ … ]วงเล็บเดี่ยวเป็นรูปแบบทางเลือกของการแสดงออกตามเงื่อนไขที่มีนิสัยใจคอมากกว่า (แต่เก่ากว่าและพกพาได้มากกว่า) อย่าเพิ่งเขียนตอนนี้; เริ่มกังวลกับพวกเขาเมื่อคุณพบสคริปต์ที่มีพวกเขา

นี่เป็นวิธีสำนวนที่จะเขียนการทดสอบของคุณใน bash

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

หากคุณต้องการความสะดวกในการพกพาไปยังเปลือกหอยอื่น ๆ นี่จะเป็นวิธี (สังเกตข้อความเพิ่มเติมและชุดของวงเล็บแยกต่างหากสำหรับการทดสอบแต่ละครั้งและการใช้=โอเปอเรเตอร์ดั้งเดิมแทนที่จะใช้==ตัวแปรksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then

31
โพสต์ยอดเยี่ยมสรุปของวงเล็บเหมาะอย่างยิ่ง
KomodoDave

10
มันจะดีกว่าที่จะใช้==เพื่อแยกความแตกต่างการเปรียบเทียบจากการกำหนดตัวแปร (ซึ่งก็คือ=)
Will Sheppard

1
โอ้ฉันหมายถึงวงเล็บเดียว (รอบ) ขอโทษสำหรับความสับสน สิ่งที่อยู่ใน[[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]นั้นไม่ได้เริ่มต้นกระบวนการย่อยถึงแม้ว่าสัญลักษณ์แสดงหัวข้อย่อยแรกจะบอกชัดเจนว่า: "มีอะไรอยู่ภายใน [วงเล็บ] ไม่ใช่นิพจน์เหมือนในภาษาอื่น ๆ " - แต่แน่นอนอยู่ที่นี่! นั่นอาจจะเห็นได้ชัดว่ามีประสบการณ์ทุบตี แต่ไม่ถึงกับฉันทันที ความสับสนสามารถเกิดขึ้นได้เพราะสามารถใช้วงเล็บเดี่ยวในifคำสั่งไม่ใช่ในนิพจน์ภายในวงเล็บเหลี่ยม
Peter - Reinstate Monica

2
@protagonist ==ไม่ใช่“ สำนวนมากกว่า” =จริงๆ พวกเขามีความหมายเดียวกัน แต่==เป็นตัวแปร ksh นอกจากนี้ยังมีในทุบตีและ zsh ในขณะที่=พกพา ไม่มีประโยชน์ใด ๆ ในการใช้งานจริง ๆ==และไม่สามารถใช้งานได้ในแบบธรรมดา
Gilles 'หยุดความชั่วร้าย'

2
@WillSheppard มีความแตกต่างอื่น ๆ อีกมากมายระหว่างการเปรียบเทียบและการมอบหมาย: การเปรียบเทียบอยู่ในวงเล็บปีกกาการมอบหมายไม่ใช่; การเปรียบเทียบมีช่องว่างรอบตัวดำเนินการกำหนดไม่มี การมอบหมายมีชื่อตัวแปรทางด้านซ้ายการเปรียบเทียบมีเพียงบางสิ่งที่ดูเหมือนชื่อตัวแปรทางซ้ายและคุณสามารถและควรใส่เครื่องหมายคำพูดอยู่ดี คุณสามารถเขียน==ข้างใน[[ … ]]หากคุณต้องการ แต่=มีข้อได้เปรียบในการทำงาน[ … ]ด้วยดังนั้นฉันขอแนะนำไม่ให้ติดนิสัยการใช้==เลย
Gilles 'หยุดความชั่วร้าย'

34

ใกล้มาก

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

ควรทำงาน.

ทำลายมันลง

[[ $varA -eq 1 ]] 

เป็นการเปรียบเทียบจำนวนเต็มโดยที่

$varB == 't1'

เป็นการเปรียบเทียบสตริง มิฉะนั้นฉันก็แค่จัดกลุ่มการเปรียบเทียบให้ถูกต้อง

เครื่องหมายวงเล็บเหลี่ยมคู่คั่นขอบเขตนิพจน์เงื่อนไข และฉันพบว่าสิ่งต่อไปนี้เป็นการอ่านที่ดีเกี่ยวกับเรื่องนี้: "(IBM) การทดสอบ Demystify, [, [, ([และ (ถ้าและจากนั้นเป็นอย่างอื่น)")


เพื่อให้แน่ใจว่า: การอ้างถึง't1'นั้นไม่จำเป็นใช่ไหม เพราะตรงข้ามกับคำแนะนำทางคณิตศาสตร์ในวงเล็บคู่ซึ่งt1จะเป็นตัวแปรt1ในการแสดงออกตามเงื่อนไขในวงเล็บคู่เป็นเพียงสตริงตัวอักษร เช่น[[ $varB == 't1' ]]นั้นเหมือนกัน[[ $varB == t1 ]]ใช่มั้ย
Peter - Reinstate Monica

6

รุ่นพกพามาก ๆ (ถึงกับ bourne shell รุ่นเก่า):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

สิ่งนี้มีคุณภาพเพิ่มเติมในการรันเพียงหนึ่ง subprocess มากที่สุด (ซึ่งเป็นกระบวนการ[) ไม่ว่าจะเป็นรสใดของเชลล์

แทนที่=ด้วย-eqหากตัวแปรมีค่าตัวเลขเช่น

  • 3 -eq 03 เป็นเรื่องจริง แต่
  • 3 = 03เป็นเท็จ (การเปรียบเทียบสตริง)

3

นี่คือรหัสสำหรับเวอร์ชั่นย่อของคำสั่ง if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

ให้ความสนใจกับสิ่งต่อไปนี้:

  1. ||และ&&ตัวถูกดำเนินการภายในหากเงื่อนไข (เช่นระหว่างวงเล็บกลม) เป็นตัวถูกดำเนินการทางตรรกะ (หรือ / และ)

  2. ||และ&&ถูกดำเนินการนอกถ้าเงื่อนไขหมายถึงแล้ว / อื่น

คำพูดบอกว่า:

ถ้า (a = 1 หรือ b = 2) ดังนั้น "ok" else "nok"


วงเล็บ( ... )สร้างเชลล์ย่อย อาจต้องการใช้เครื่องมือจัดฟัน{ ... }แทน สถานะใด ๆ ที่สร้างขึ้นใน subshell จะไม่ปรากฏในผู้โทร
Clint Pachl

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