ฉันจะประกาศและใช้ตัวแปรบูลีนในเชลล์สคริปต์ได้อย่างไร


978

ฉันพยายามประกาศตัวแปรบูลีนในเชลล์สคริปต์โดยใช้ไวยากรณ์ต่อไปนี้:

variable=$false

variable=$true

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

if [ $variable ]

if [ !$variable ]

73
ระวัง! trueและfalseในบริบทของตัวอย่างส่วนใหญ่ที่อยู่ด้านล่างเป็นเพียงสตริงธรรมดาไม่bash built-ins !!! โปรดอ่านคำตอบของ Mike Holt ด้านล่าง (นี่คือตัวอย่างหนึ่งที่คำตอบที่ได้รับการโหวตอย่างสูงและได้รับการยอมรับคือ IMHO ทำให้เกิดความสับสนและเงาเนื้อหาที่ลึกซึ้งในคำตอบที่ได้รับการโหวตน้อยกว่า)
mjv

7
@mjv ความสับสนส่วนใหญ่เกี่ยวกับคำถามนี้ (และคำตอบของ Miku) เนื่องจากข้อเท็จจริงที่ว่า Miku ได้แก้ไขคำตอบของเขาในบางจุดหลังจากความคิดเห็นหลายครั้งถูกโพสต์อธิบายว่าคำตอบของ Miku เกี่ยวข้องกับการเรียก bash ในตัวtrueอย่างไร กลับกลายเป็นคำตอบเดิม Miku จริงๆไม่เรียกtrueในตัว แต่คำตอบที่แก้ไขไม่ได้ สิ่งนี้ทำให้ความเห็นที่กล่าวมาดูเหมือนจะผิดเกี่ยวกับวิธีการทำงานของรหัส Miku คำตอบของ Miku นั้นได้รับการแก้ไขเพื่อแสดงทั้งรหัสดั้งเดิมและรหัสที่แก้ไขแล้วอย่างชัดเจน หวังว่านี่จะทำให้เกิดความสับสนในการพักผ่อนทันที
Mike Holt

2
[ true ] && echo yes, true is trueและ [ false ] && echo yes, false is also true(upppsss) / bin / true และ / bin / false ให้โค้ดส่งคืน $? สำหรับฟังก์ชั่นที่ไม่ได้ทำการเปรียบเทียบ
fcm

ถ้า var ถูกตั้งค่าvariable=somethingมากกว่าที่เป็นจริงถ้า unset variable=ที่จะเป็นเท็จ[[ $variable ]] && echo true || echo falseและย้อนกลับ[[ ! $variable ]] && echo false || echo true
Ivan

คำตอบ:


1201

แก้ไขคำตอบแล้ว (12 ก.พ. 2014)

the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
    echo 'Be careful not to fall off!'
fi

คำตอบเดิม

Caveats: https://stackoverflow.com/a/21210966/89391

the_world_is_flat=true
# ...do something interesting...
if $the_world_is_flat ; then
    echo 'Be careful not to fall off!'
fi

จาก: การใช้ตัวแปรบูลีนใน Bash

เหตุผลที่คำตอบดั้งเดิมรวมอยู่ที่นี่เป็นเพราะความคิดเห็นก่อนการแก้ไขในวันที่ 12 กุมภาพันธ์ 2014 เกี่ยวข้องกับคำตอบเดิมเท่านั้นและความคิดเห็นจำนวนมากผิดเมื่อเชื่อมโยงกับคำตอบที่แก้ไขแล้ว ตัวอย่างเช่นความคิดเห็นของเดนนิสวิลเลียมสันเกี่ยวกับ bash builtin trueในวันที่ 2 มิถุนายน 2010 จะใช้กับคำตอบดั้งเดิมเท่านั้น


37
ที่จะอธิบายสิ่งที่เกิดขึ้นคือifคำสั่งรันเนื้อหาของตัวแปรซึ่งเป็น truebuiltin คำสั่งใด ๆ ที่สามารถตั้งค่าเป็นค่าของตัวแปรและค่าออกจากมันจะได้รับการประเมิน
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

7
@pms ตัวดำเนินการ "-o" และ "-a" สำหรับคำสั่ง "test" เท่านั้น (aka "[]") นี่คือ "if + command" โดยไม่มี "test" (. ชอบ "ถ้าไฟล์ grep foo แล้ว ...") ดังนั้นการใช้ปกติ&&และ||ผู้ประกอบการ: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (ผลตอบแทน "จริง" เนื่องจาก t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (ผลตอบแทน "จริง" ตั้งแต่ T2 = true) อีกครั้งนี้ใช้งานได้เพราะ "true" / "false" เป็น bash-builtins (คืนค่าจริง / เท็จ) คุณไม่สามารถใช้ "ถ้า $ var ..." เว้นแต่ var เป็น cmd (คือจริงหรือเท็จ)
ไมเคิล

14
-1 ดูคำตอบของฉันสำหรับคำอธิบาย
Dennis

3
มีข้อมูลที่ไม่ถูกต้องมากมายที่นี่ / bin / true ไม่ถูกใช้อย่างมีประสิทธิภาพ ดูคำตอบของเดนนิส
ajk

1
รหัสนี้ไม่เหมือนกันและไม่ทำงานในลักษณะเดียวกับบทความที่เชื่อมโยง รหัสที่เชื่อมโยงเรียกโปรแกรมด้วยชื่อที่เก็บไว้ในตัวแปร แต่รหัสในคำตอบนี้เป็นเพียงการเปรียบเทียบสตริง
คำถามของ Quolonel เมื่อ

794

TL; DR

bool=true

if [ "$bool" = true ]

ปัญหาเกี่ยวกับคำตอบของ Miku ( ต้นฉบับ )

ฉันจะไม่แนะนำให้คำตอบที่ได้รับการยอมรับ1 ไวยากรณ์ของมันค่อนข้างสวย แต่ก็มีข้อบกพร่องอยู่บ้าง

สมมติว่าเรามีเงื่อนไขดังต่อไปนี้

if $var; then
  echo 'Muahahaha!'
fi

ในกรณีต่อไปนี้2เงื่อนไขนี้จะประเมินเป็นจริงและดำเนินการคำสั่งที่ซ้อนกัน

# Variable var not defined beforehand. Case 1
var=''  # Equivalent to var="".        Case 2
var=    #                              Case 3
unset var  #                           Case 4
var='<some valid command>'  #          Case 5

โดยทั่วไปคุณต้องการให้เงื่อนไขของคุณประเมินเป็นจริงเมื่อตัวแปร "บูลีน" ของคุณvarในตัวอย่างนี้ถูกตั้งค่าเป็นจริงเท่านั้น กรณีอื่น ๆ ทั้งหมดทำให้เข้าใจผิดอย่างอันตราย!

กรณีสุดท้าย (# 5) มีความซนโดยเฉพาะอย่างยิ่งเพราะจะดำเนินการคำสั่งที่มีอยู่ในตัวแปร (ซึ่งเป็นสาเหตุที่เงื่อนไขประเมินเป็นจริงสำหรับคำสั่งที่ถูกต้อง3, 4 )

นี่คือตัวอย่างที่ไม่เป็นอันตราย:

var='echo this text will be displayed when the condition is evaluated'
if $var; then
  echo 'Muahahaha!'
fi

# Outputs:
# this text will be displayed when the condition is evaluated
# Muahahaha!

if "$var"; thenเธซเธฑตัวแปรของคุณจะปลอดภัยมากขึ้นเช่น ในกรณีข้างต้นคุณควรได้รับคำเตือนว่าไม่พบคำสั่ง แต่เราสามารถทำได้ดีกว่า (ดูคำแนะนำของฉันที่ด้านล่าง)

ดูคำอธิบายของ Mike Holt ต่อคำตอบดั้งเดิมของ Miku ด้วย

ปัญหาเกี่ยวกับคำตอบของ Hbar

วิธีการนี้ยังมีพฤติกรรมที่ไม่คาดคิด

var=false
if [ $var ]; then
  echo "This won't print, var is false!"
fi

# Outputs:
# This won't print, var is false!

คุณคาดว่าเงื่อนไขข้างต้นจะประเมินเป็นเท็จดังนั้นอย่าดำเนินการคำสั่งที่ซ้อนกัน เซอร์ไพร์ส!

การอ้างถึงค่า ( "false"), การอ้างถึงตัวแปร ( "$var"), หรือการใช้testหรือ[[แทน[, อย่าสร้างความแตกต่าง

สิ่งที่ฉันไม่แนะนำ:

นี่คือวิธีที่ฉันแนะนำให้คุณตรวจสอบ "บูลีน" ของคุณ พวกเขาทำงานตามที่คาดไว้

bool=true

if [ "$bool" = true ]; then
if [ "$bool" = "true" ]; then

if [[ "$bool" = true ]]; then
if [[ "$bool" = "true" ]]; then
if [[ "$bool" == true ]]; then
if [[ "$bool" == "true" ]]; then

if test "$bool" = true; then
if test "$bool" = "true"; then

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


เชิงอรรถ

  1. คำตอบของ Miku นั้นได้รับการแก้ไขและไม่มีข้อบกพร่อง
  2. ไม่ใช่รายการที่ครบถ้วนสมบูรณ์
  3. คำสั่งที่ถูกต้องในบริบทนี้หมายถึงคำสั่งที่มีอยู่ ไม่สำคัญว่าจะใช้คำสั่งอย่างถูกต้องหรือไม่ถูกต้อง เช่นman womanจะยังคงถือว่าเป็นคำสั่งที่ถูกต้องแม้ว่าจะไม่มี man page ดังกล่าวอยู่ก็ตาม
  4. สำหรับคำสั่งที่ไม่ถูกต้อง (ไม่มีอยู่) Bash จะบ่นว่าไม่พบคำสั่ง
  5. หากคุณใส่ใจเรื่องความยาวข้อแนะนำแรกนั้นสั้นที่สุด

8
ใช้==กับ[หรือtestไม่พกพาได้ พิจารณาการพกพาเป็นประโยชน์เฉพาะ[/ testมีกว่าติดกับ[[ =
chepner

2
@Scott ฉันใช้ปลาเป็นกระสุนหลักของฉันซึ่งมีภาษาสคริปต์ที่ดีเทียบกับทุบตีในความคิดของฉัน
Dennis

1
ใช่ครับผมก็ไม่สามารถหาได้ในการแสดงความคิดเห็นชื่นชมใด ๆ สำหรับเรื่องตลกซ่อนนี้เพื่อให้มีการชี้ออก =)
Kranach

5
สำหรับฉันแล้วแนวคิดจะเข้าใจได้ง่ายขึ้นถ้าฉันใช้ bool = "true" เห็นได้ชัดว่ามันเป็นเพียงแค่สตริงไม่ใช่ค่าพิเศษหรือ builtin
wisbucky

1
@dolmen การประเมินการป้อนข้อมูลนั้นไม่ได้มีความเสี่ยงเมื่อคุณควบคุมอินพุต แต่ฉันก็ยังถือว่ามันเป็นวิธีปฏิบัติที่ไม่ดีที่ควรหลีกเลี่ยงหากสามารถหลีกเลี่ยงได้อย่างง่ายดาย ใครบางคนที่เคยเห็นและใช้รูปแบบในอดีตอาจไม่รู้เกี่ยวกับข้อบกพร่องซึ่งอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด
Dennis

175

ดูเหมือนว่ามีบางคนเข้าใจผิดเกี่ยวกับ Bash builtin trueและที่เฉพาะเจาะจงมากขึ้นเกี่ยวกับวิธีที่ Bash ขยายและตีความนิพจน์ภายในวงเล็บ

รหัสในคำตอบของ mikuนั้นไม่มีอะไรเกี่ยวข้องกับ Bash builtin trueหรือ/bin/trueอย่างใดtrueอย่างหนึ่ง ในกรณีtrueนี้ไม่มีอะไรมากไปกว่าสตริงอักขระแบบง่าย ๆ และไม่มีการเรียกtrueคำสั่ง / บิลด์อินใด ๆ เลยไม่ว่าจะเป็นการกำหนดตัวแปรหรือการประเมินนิพจน์เงื่อนไข

รหัสต่อไปนี้เป็นหน้าที่เหมือนกับรหัสในคำตอบของ miku:

the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
    echo 'Be careful not to fall off!'
fi

ข้อแตกต่างเพียงอย่างเดียวที่นี่คือการเปรียบเทียบอักขระสี่ตัวคือ 'y', 'e', ​​'a' และ 'h' แทน 't', 'r', 'u' และ 'e' แค่นั้นแหละ. มีความพยายามไม่ทำเพื่อเรียกคำสั่งหรือการ builtin ชื่อเป็นyeahและไม่มี (ในตัวอย่างของ Miku) เรียงลำดับใด ๆ trueของการจัดการพิเศษที่เกิดขึ้นเมื่อทุบตีแยกโทเค็น มันเป็นเพียงแค่สตริงและหนึ่งในนั้น

Update (2014-02-19):หลังจากติดตามลิงก์ในคำตอบของ miku ตอนนี้ฉันเห็นว่าความสับสนมาจากไหน คำตอบของ Miku ใช้เครื่องหมายวงเล็บเดียว แต่ข้อมูลโค้ดที่เขาลิงก์ไปนั้นไม่ได้ใช้วงเล็บ มันเป็นเพียง:

the_world_is_flat=true
if $the_world_is_flat; then
  echo 'Be careful not to fall off!'
fi

ตัวอย่างโค้ดทั้งสองจะทำงานในลักษณะเดียวกัน แต่วงเล็บจะเปลี่ยนสิ่งที่เกิดขึ้นภายใต้ประทุนอย่างสมบูรณ์

นี่คือสิ่งที่ Bash ทำในแต่ละกรณี:

ไม่มีวงเล็บ:

  1. ขยายตัวแปรสตริง$the_world_is_flat"true"
  2. พยายามแยกสตริง"true"เป็นคำสั่ง
  3. ค้นหาและเรียกใช้trueคำสั่ง (ทั้งในตัวหรือ/bin/trueขึ้นอยู่กับรุ่น Bash)
  4. เปรียบเทียบรหัสทางออกของtrueคำสั่ง (ซึ่งเป็น 0 เสมอ) กับ 0 เรียกคืนว่าในเชลล์ส่วนใหญ่รหัสทางออก 0 หมายถึงความสำเร็จและสิ่งอื่น ๆ บ่งชี้ความล้มเหลว
  5. เนื่องจากรหัสออกเป็น 0 (สำเร็จ) ให้รันประโยคifของthenคำสั่ง

วงเล็บ:

  1. ขยายตัวแปรสตริง$the_world_is_flat"true"
  2. string1 = string2แยกนิพจน์เงื่อนไขในขณะนี้อย่างเต็มที่ขยายตัวซึ่งเป็นของแบบฟอร์ม =ผู้ประกอบการคือการทุบตีของการเปรียบเทียบสตริงประกอบการ ดังนั้น...
  3. ทำเปรียบเทียบสตริงและ"true""true"
  4. ใช่ทั้งสองสายเหมือนกันดังนั้นค่าของเงื่อนไขเป็นจริง
  5. ดำเนินการifคำสั่งของthenประโยค

รหัส no-brackets ทำงานได้เนื่องจากtrueคำสั่งส่งคืนรหัสออกเป็น 0 ซึ่งบ่งชี้ความสำเร็จ รหัสวงเล็บทำงานเพราะค่าของ$the_world_is_flatเป็นเหมือนตัวอักษรสตริงที่ด้านขวาของtrue=

เพียงขับรถกลับบ้านให้พิจารณาโค้ดสองตัวอย่างต่อไปนี้:

รหัสนี้ (หากทำงานด้วยสิทธิ์พิเศษระดับราก) จะรีบูตคอมพิวเตอร์ของคุณ:

var=reboot
if $var; then
  echo 'Muahahaha! You are going down!'
fi

รหัสนี้แค่พิมพ์ "ลองดี" คำสั่ง reboot ไม่ได้ถูกเรียก

var=reboot
if [ $var ]; then
  echo 'Nice try.'
fi

Update (2014-04-14)เพื่อตอบคำถามในความคิดเห็นเกี่ยวกับความแตกต่างระหว่าง=และ==: AFAIK ไม่มีความแตกต่าง ==ประกอบเป็นคำพ้องทุบตีที่เฉพาะเจาะจงสำหรับ=, และเท่าที่ผมเคยเห็นพวกเขาทำงานเหมือนกันในทุกบริบท

อย่างไรก็ตามโปรดทราบว่าฉันกำลังพูดถึงตัวดำเนินการเปรียบเทียบ=และ==สตริงโดยเฉพาะที่ใช้ในการทดสอบ[ ]หรือ [[ ]]ฉันไม่ได้แนะนำสิ่งนั้น=และ==สามารถใช้แทนกันได้ทุกที่ในการทุบตี

ตัวอย่างเช่นคุณไม่สามารถกำหนดค่าตัวแปรได้==อย่างชัดเจนเช่นvar=="foo"(ในทางเทคนิคคุณสามารถทำสิ่งนี้ได้ แต่ค่าvarจะเป็น"=foo"เพราะ Bash ไม่เห็น==ผู้ประกอบการที่นี่เห็นผู้ดำเนินการ=(ที่ได้รับมอบหมาย) ตามด้วย ค่าตัวอักษร="foo"ซึ่งเพิ่งจะกลายเป็น"=foo")

นอกจากนี้แม้ว่า=และ==สามารถใช้แทนกันคุณควรเก็บไว้ในใจว่าวิธีการที่ทำงานการทดสอบไม่ขึ้นอยู่กับว่าคุณกำลังใช้ภายใน[ ]หรือ[[ ]]และยังหรือไม่ว่าตัวถูกดำเนินการที่มีการอ้าง คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสิ่งนั้นได้ในคู่มือการใช้สคริปต์การทุบตีขั้นสูง: 7.3 ตัวดำเนินการเปรียบเทียบอื่น ๆ (เลื่อนลงไปที่การอภิปราย=และ==)


วิธีการแบบไม่มีวงเล็บยังมีข้อได้เปรียบของการให้คุณเขียนหนึ่ง - liners สะอาด, ชัดเจน (imo)$the_world_is_flat && echo "you are in flatland!"
ajk

9
จริง แม้ว่าฉันจะไม่สนับสนุน (หรือต่อต้าน) วิธีการอย่างใดอย่างหนึ่ง ฉันแค่ต้องการล้างข้อมูลที่ผิดที่ได้รับการโหวตที่นี่เพื่อให้ผู้ที่สะดุดกับหัวข้อนี้ในภายหลังจะไม่เดินไปพร้อมกับความเข้าใจผิดมากมายเกี่ยวกับวิธีการทำงานทั้งหมดนี้
Mike Holt

1
เหตุผลของความสับสนก็คือคำตอบดั้งเดิมของ miku นั้นยืนอยู่ 4 ปี การอ้างอิงทั้งหมดไปยังบิวด์อินtrueนั้นทำขึ้นโดยคำนึงถึงคำตอบดั้งเดิม (คำตอบที่แก้ไขแล้วเมื่อวันที่ 12 ก.พ. 2014 ไม่ได้ถูกส่งโดยมิคุ) ฉันได้แก้ไขคำตอบเพื่อให้รวมถึงต้นฉบับและฉบับแก้ไขแล้ว จากนั้นความคิดเห็นของผู้คนก็สมเหตุสมผล
wisbucky

1
trueจากการอ่านคำตอบที่นำเสนอที่นี่ผมได้รับความประทับใจว่าไม่มีสิ่งดังกล่าวเป็นจริงโดยใช้จริง มีวิธีหรือไม่? ฉันสงสัยว่าโปรแกรมเมอร์หลายคนที่คุ้นเคยกับการใช้ภาษาที่เข้มงวดในการดูคำตอบนี้เพื่อช่วยพวกเขาในการผสมbashกาวเพื่อทำให้ชีวิตของพวกเขาง่ายขึ้นสักหน่อยอยากให้===โอเปอเรเตอร์เพื่อที่ว่าสตริงและ "บูลีน" พวกเขาก็ควรจะติด 0 และ 1 และการใช้งาน(( $maybeIAmTrue ))ตามข้อเสนอแนะในQuolonel คำถามของคำตอบ ?
SeldomNeedy

2
เพื่อพูดถึงความคิดเห็นของ SeldomNeedy ได้คุณสามารถใช้ของจริงtrueแต่โดยทั่วไปไม่ได้เป็นสิ่งที่เปรียบเทียบตัวแปรเนื่องจากของจริงtrueไม่มีค่าต่อ ทั้งหมดจะถูกตั้งค่าสถานะการออกเป็น0แสดงความสำเร็จ มันน่าสังเกตว่ามันเป็นหลักเทียบเท่ากับที่เรียกว่า "คำสั่ง null" :หรือ เท่าที่ใช้0และ1นั่นคือสิ่งที่ฉันทำในสคริปต์ของฉันทุกวันนี้ที่ฉันต้องการบูลีน และฉันใช้(( ))โอเปอเรเตอร์แทน[[ ]]การประเมิน ตัวอย่างเช่นถ้าฉันมีflag=0ฉันก็ทำได้if (( flag )); then ...
Mike Holt

57

ใช้นิพจน์ทางคณิตศาสตร์

#!/bin/bash

false=0
true=1

((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true

เอาท์พุท:

จริง
ไม่เท็จ


3
ข้อดี: (1. ) พฤติกรรมคล้ายกับวิธีของ C ในการจัดการบูลส์ (2. ) ไวยากรณ์มีความรัดกุมมาก / น้อยที่สุด (ไม่ต้องการตัวแปรทางขวาและตัวดำเนินการเช่น '=' หรือ '=='), (3 .) <subjective> สำหรับฉันฉันเข้าใจว่าจะเกิดอะไรขึ้นหากปราศจากคำอธิบายที่ยืดยาว ... ตรงกันข้ามกับคำตอบของ Miku และ Dennis ซึ่งทั้งคู่ดูเหมือนจะต้องการคำอธิบายที่ยืดยาว </subjective>
เทรเวอร์บอยด์สมิ ธ

3
@ TrevorBoydSmith ทำไมคุณไม่พูดว่า "pros: all, cons: nothing" จะช่วยประหยัดค่าเสื่อมราคาบนคีย์บอร์ดและจอมอนิเตอร์ของคุณในระยะยาว
คำถามของ Quolonel

4
สำหรับการใช้งานแบบอินเทอร์แอคทีฟเช่นตอร์ปิโดต้องแน่ใจว่าได้เว้นช่องว่าง!ไว้ไม่เช่นนั้นจะเป็นการขยายประวัติ ((! foo))ทำงาน! ((foo))ได้ ฉันรักโซลูชันนี้ BTW ในที่สุดวิธีรัดกุมในการทำตัวแปรบูลีน ((foo || bar))ทำงานตามที่คาดไว้
Peter Cordes

5
(())ขยายตัวแปรแบบเรียกซ้ำซึ่งฉันไม่ได้คาดหวัง foo=bar; bar=baz; ((foo)) && echo echoพิมพ์อะไร baz=1แต่มันเป็นความจริงที่มี ดังนั้นคุณสามารถสนับสนุนfoo=trueและfoo=falseเช่นเดียวกับ 0 หรือ 1 true=1โดยการทำ
Peter Cordes

2
@quolonel ขอบคุณสำหรับทรัพยากรที่มีประโยชน์มาก แน่นอนความเข้าใจของฉันมี จำกัด - เป็นธรรมชาติของมนุษย์ที่จะถูก จำกัด ในการทำความเข้าใจทั้งหมดไม่ว่าโดเมน อย่างไรก็ตามคุณจะช่วยบอกฉันว่าข้อความใดของฉันที่นำคุณไปสู่การสันนิษฐานว่าการเข้าใจในเรื่องนี้ไม่สมบูรณ์หรือไม่?
Hubert Grzeskowiak

34

เรื่องสั้นสั้น:

ไม่มี Booleans ใน Bash

Bash มีนิพจน์บูลีนในแง่ของการเปรียบเทียบและเงื่อนไข ที่กล่าวว่าสิ่งที่คุณสามารถประกาศและเปรียบเทียบใน Bash คือสตริงและตัวเลข แค่นั้นแหละ.

ไม่ว่าคุณจะเห็นtrueหรือfalseอยู่ใน Bash มันเป็นสตริงหรือคำสั่ง / builtin ซึ่งใช้สำหรับรหัสการออกเท่านั้น

ไวยากรณ์นี้ ...

if true; then ...

เป็นหลัก ...

if COMMAND; then ...

เงื่อนไขเป็นจริงเมื่อใดก็ตามที่คำสั่งส่งคืนรหัสออก 0 trueและfalseเป็น Bash builtins และบางครั้งก็เป็นโปรแกรมแบบสแตนด์อโลนที่ไม่ทำอะไรเลยนอกจากส่งคืนรหัสออกที่สอดคล้องกัน

เงื่อนไขข้างต้นเทียบเท่ากับ:

COMMAND && ...

เมื่อใช้วงเล็บเหลี่ยมหรือtestคำสั่งคุณต้องพึ่งพารหัสออกของการสร้างนั้น โปรดทราบว่า[ ]และ[[ ]]เป็นเพียงคำสั่ง / บิลด์อินเหมือน ๆ กัน ดังนั้น ...

if [[ 1 == 1 ]]; then echo yes; fi

สอดคล้องกับ

if COMMAND; then echo yes; fi

และCOMMANDนี่คือ[[ 1 == 1 ]]

if..then..fiสร้างเป็นเพียงน้ำตาลประโยค คุณสามารถเรียกใช้คำสั่งที่คั่นด้วยเครื่องหมายแอมเปอร์แซนด์และเอฟเฟกต์เดียวกันได้ตลอดเวลา:

[[ 1 == 1 ]] && echo yes

เมื่อใช้trueและfalseในการสร้างการทดสอบเหล่านี้คุณจะผ่านสตริง"true"หรือ"false"คำสั่งการทดสอบเท่านั้น นี่คือตัวอย่าง:

เชื่อหรือไม่ แต่เงื่อนไขเหล่านั้นล้วนแล้วแต่ให้ผลลัพธ์ที่เหมือนกัน:

if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...

TL; DR; เปรียบเทียบกับสตริงหรือตัวเลขเสมอ

เพื่อให้ชัดเจนกับผู้อ่านในอนาคตฉันขอแนะนำให้ใช้เครื่องหมายคำพูดเสมอtrueและfalse:

ทำ

if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...

ไม่

if [ ... ]; then ...  # Always use double square brackets in bash!
if [[ "${var}" ]]; then ...  # This is not as clear or searchable as -n
if [[ "${var}" != true ]]; then ...  # Creates impression of Booleans
if [[ "${var}" -eq "true" ]]; then ...  # `-eq` is for numbers and doesn't read as easy as `==`

อาจจะ

if [[ "${var}" != "true" ]]; then ...  # Creates impression of Booleans. It can be used for strict checking of dangerous operations. This condition is false for anything but the literal string "true".

ฉันชอบที่จะใช้TและFเพื่อให้ชัดเจนว่าสิ่งเหล่านั้นไม่ใช่ค่าบูลีนจริง
phk

1
ฉันไม่สามารถเห็นด้วยกับ "ใช้วงเล็บคู่ในการทุบตีเสมอ" อันที่จริงในสคริปต์เกือบทั้งหมดที่ฉันเขียนฉันใช้วงเล็บเดียวยกเว้นเมื่อฉันต้องทำการจับคู่รูปแบบ ฉันคิดว่าควรเข้าใจความแตกต่างระหว่าง[(เช่นtest) [[และใช้สิ่งที่เหมาะสมกับความต้องการของเขา
Weijun Zhou

@ WeijunZhou ใจบรรจงในกรณีที่วงเล็บเดียวดีกว่าหรือไม่
Hubert Grzeskowiak

มันเป็นรสนิยมส่วนตัวมากกว่าฉันเพิ่งพบว่ามันกล้าเกินกว่าจะพูดว่า "ควรใช้วงเล็บเหลี่ยมสองอันในการทุบตี" แต่มีบางกรณีที่ฉันได้ใช้ขอบ วงเล็บเดียวอนุญาตให้คุณระบุการทดสอบด้วยตัวเองใน var ในฐานะตัวอย่างที่มีขนาดใหญ่เกินไปให้พิจารณาif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Weijun Zhou

@ WeijunZhou ตัวอย่างของคุณเป็นข้อโต้แย้งที่แข็งแกร่งสำหรับวงเล็บเหลี่ยมเดี่ยว มันทำให้รหัสยากขึ้นในการทำความเข้าใจและเปิดหน้าต่างเพื่อเปิดข้อผิดพลาด วงเล็บคู่มีความเข้มงวดมากขึ้นและสนับสนุนให้โค้ดที่สะอาดยิ่งขึ้น
Hubert Grzeskowiak

18

นานมาแล้วเมื่อเรามีทั้งหมดshBooleans ที่จัดการโดยอาศัยการประชุมของtestโปรแกรมที่testส่งกลับสถานะทางออกที่ผิดถ้าวิ่งโดยไม่มีข้อโต้แย้งใด ๆ

สิ่งนี้อนุญาตให้คิดว่าตัวแปรที่ไม่ได้ตั้งค่าเป็นเท็จและตัวแปรถูกตั้งค่าเป็นจริง วันนี้testเป็น builtin เพื่อ Bash และเป็นที่รู้จักกันทั่วไปโดยนามแฝงตัวละครตัวหนึ่งของมัน[(หรือปฏิบัติการที่จะใช้ในเปลือกหอยขาดมันเป็นบันทึก dolmen):

FLAG="up or <set>"

if [ "$FLAG" ] ; then
    echo 'Is true'
else
    echo 'Is false'
fi

# Unset FLAG
#    also works
FLAG=

if [ "$FLAG" ] ; then
    echo 'Continues true'
else
    echo 'Turned false'
fi

เนื่องจากข้อตกลงการอ้างถึงผู้เขียนสคริปต์ต้องการใช้คำสั่งผสม[[ที่เลียนแบบtestแต่มีไวยากรณ์ที่ดีกว่า: ตัวแปรที่มีช่องว่างไม่จำเป็นต้องอ้าง หนึ่งสามารถใช้&&และ||เป็นตัวดำเนินการเชิงตรรกะที่มีความสำคัญแปลก ๆ และไม่มีข้อ จำกัด POSIX กับจำนวนคำ

ตัวอย่างเช่นเพื่อตรวจสอบว่ามีการตั้งค่า FLAG หรือไม่และ COUNT เป็นตัวเลขที่มากกว่า 1:

FLAG="u p"
COUNT=3

if [[ $FLAG  && $COUNT -gt '1' ]] ; then
    echo 'Flag up, count bigger than 1'
else
    echo 'Nope'
fi

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


3
[bashไม่ได้เป็นเพียงนามแฝงภายใน นามแฝงนี้ยังมีอยู่เป็นไฟล์ไบนารี (หรือเป็นลิงก์ที่ชี้ไป) และสามารถใช้กับไฟล์เปล่าshได้ ls -l /usr/bin/\[ตรวจสอบ ด้วยbash/ zshคุณควรใช้[[สิ่งนี้ว่าเป็นสิ่งที่บริสุทธิ์อย่างแท้จริงภายในและมีพลังมากกว่า
dolmen

1
@dolmen [และtestยังเป็นคำสั่งของเชลล์ Bash BUILTIN ตามหน้าคู่มือของ Bash ดังนั้นจึงไม่ควรมีปัญหาเรื่องประสิทธิภาพ สิ่งเดียวกันกับเช่น Dash (/ bin / sh อาจเป็น symlink ไปยัง / bin / dash) /usr/bin/\[ที่จะใช้ปฏิบัติการที่คุณต้องใช้คือเส้นทางแบบเต็ม
jarno

12

ฉันจะประกาศและใช้ตัวแปรบูลีนในเชลล์สคริปต์ได้อย่างไร

ไม่เหมือนกับภาษาโปรแกรมอื่น ๆ อีกมากมาย Bash ไม่ได้แยกตัวแปรตาม "type" [1]

ดังนั้นคำตอบนั้นค่อนข้างชัดเจน ไม่มีตัวแปรบูลีนใน Bash

อย่างไรก็ตาม:

การใช้คำสั่งประกาศเราสามารถ จำกัด การกำหนดค่าให้กับตัวแปร [2]

#!/bin/bash
declare -ir BOOL=(0 1) # Remember BOOL can't be unset till this shell terminates
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}

# Same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"

rตัวเลือกในdeclareและreadonlyถูกนำมาใช้เพื่อให้รัฐอย่างชัดเจนว่าตัวแปรที่มีอ่านได้อย่างเดียว ฉันหวังว่าวัตถุประสงค์จะชัดเจน


1
ทำไมคุณไม่เพียงแค่ทำdeclare -ir false=0 true=1? ข้อดีของการใช้อาร์เรย์คืออะไร
Benjamin W.

@BenjaminW ฉันแค่อยากจะพูดถึงเกี่ยวกับrตัวเลือก & readonlyคำสั่ง ฉันจะทำตามวิธีที่คุณแนะนำในสคริปต์ของฉัน
sjsam

บางทีฉันอาจพลาดบางสิ่ง แต่ทำไมไม่ประกาศจริงและเท็จด้วยวิธีนี้ให้ใช้เครื่องหมายดอลลาร์? $ true $ false
qodeninja

แท้จริงเพียงคัดลอกคำตอบของฉันและทำให้แย่ลง
คำถามของ

ตัวแปร @QuolonelQuestions ทุบตีไม่ได้พิมพ์จึงมีจุดในการบอกว่าไม่มีdeclare and use boolean variablesเราก็สามารถในทางมากกว่าหนึ่งเลียนแบบ / สมมติว่าตัวแปรที่มี ชนิด ฉันไม่เห็นคำตอบของคุณ
sjsam

10

แทนที่จะเลียนแบบบูลีนและทิ้งกับดักไว้สำหรับผู้อ่านในอนาคตทำไมไม่ใช้แค่คุณค่าที่ดีกว่าความจริงและเท็จ?

ตัวอย่างเช่น:

build_state=success
if something-horrible; then
  build_state=failed
fi

if [[ "$build_state" == success ]]; then
  echo go home; you are done
else
  echo your head is on fire; run around in circles
fi

ทำไมไม่เป็นจำนวนเต็ม?
phil294

3
@Blauhirn เนื่องจากมีการใช้จำนวนเต็มแตกต่างกันไปขึ้นอยู่กับภาษา ในบางภาษา0coerces ไปfalseและจะ1 trueในเรื่องที่เกี่ยวกับรหัสโปรแกรมออก (ซึ่งในอดีตใช้ทุบตี) มัน0สำหรับผลบวกหรือtrueและทุกสิ่งทุกอย่างที่เป็นลบ / falseหรือข้อผิดพลาด
Hubert Grzeskowiak

7

POSIX (อินเตอร์เฟสของระบบปฏิบัติการพกพา)

ฉันคิดถึงจุดสำคัญซึ่งเป็นความสะดวกในการพกพา นั่นเป็นสาเหตุที่ส่วนหัวของฉันมีPOSIXอยู่ในตัวของมันเอง

โดยพื้นฐานแล้วทั้งหมดของคำตอบที่ได้รับการโหวตให้ถูกต้องด้วยข้อยกเว้นที่พวกเขาจะทุบตี -specific มากเกินไป

โดยทั่วไปฉันเพียงต้องการเพิ่มข้อมูลเพิ่มเติมเกี่ยวกับการพกพา


  1. [และ]วงเล็บเหลี่ยมใน[ "$var" = true ]ไม่จำเป็นและคุณสามารถละเว้นและใช้testคำสั่งโดยตรง:

    test "$var" = true && yourCodeIfTrue || yourCodeIfFalse

    หมายเหตุสำคัญ: ฉันไม่แนะนำให้ใช้สิ่งนี้เนื่องจากมันเลิกใช้ช้าและยากที่จะรวมหลาย ๆ คำสั่ง

  2. ลองนึกภาพสิ่งที่คำเหล่านั้นtrueและfalseหมายถึงเปลือกหอยทดสอบตัวเอง:

    echo $(( true ))
    0
    echo $(( false ))
    1

    แต่ใช้เครื่องหมายคำพูด:

    echo $(( "true" ))
    bash: "true": syntax error: operand expected (error token is ""true"")
    sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""

    เช่นเดียวกับ:

    echo $(( "false" ))

    เชลล์ไม่สามารถตีความมันนอกเหนือจากสตริง ฉันหวังว่าคุณจะได้รับความคิดของวิธีการที่ดีก็คือการใช้คำหลักที่เหมาะสมไม่ทราบราคา

    แต่ไม่มีใครพูดถึงมันในคำตอบก่อนหน้า

  3. สิ่งนี้หมายความว่า? หลายสิ่งหลายอย่าง

    • คุณควรทำความคุ้นเคยกับคีย์เวิร์ดบูลีนจริง ๆ แล้วจะถือว่าเป็นตัวเลขนั่นคือtrue= 0และfalse= 1โปรดจำไว้ว่าค่าที่ไม่เป็นศูนย์ทั้งหมดจะถือว่าเป็นเช่นfalseนั้น

    • เนื่องจากพวกเขาถือว่าเป็นตัวเลขคุณควรปฏิบัติต่อพวกเขาเช่นนั้นเช่นถ้าคุณกำหนดตัวแปรพูดว่า:

      var_bool=true
      echo "$var_bool"
       true

      คุณสามารถสร้างมูลค่าที่ตรงกันข้ามได้ด้วย:

      var_bool=$(( 1 - $var_bool ))  # same as $(( ! $var_bool ))
      echo "$var_bool"
      1

    อย่างที่คุณเห็นด้วยตัวเองเชลล์จะพิมพ์trueสตริงเป็นครั้งแรกที่คุณใช้ แต่ตั้งแต่นั้นมามันจะทำงานผ่านหมายเลขที่0เป็นตัวแทนtrueหรือ1เป็นตัวแทนfalseตามลำดับ


ในที่สุดสิ่งที่คุณควรทำกับข้อมูลทั้งหมดที่

  • ครั้งแรกหนึ่งนิสัยที่ดีจะได้รับการกำหนด0แทนtrue; แทน1false

  • นิสัยที่ดีอันดับสองคือการทดสอบว่าตัวแปรนั้น / ไม่เท่ากับศูนย์:

    if [ "$var_bool" -eq 0 ]; then
         yourCodeIfTrue
    else
         yourCodeIfFalse
    fi

6

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

# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc

if [[ -n "${var}" ]] ; then
    echo 'true'
fi
if [[ -z "${var}" ]] ; then
    echo 'false'
fi

# Results
# var=        # false
# var=''      # false
# var=""      # false
# var=0       # true
# var=1       # true
# var="abc"   # true
# var=abc     # true

หากไม่เคยประกาศตัวแปรคำตอบคือ: # false

ดังนั้นวิธีง่ายๆในการตั้งค่าตัวแปรให้เป็นจริง (โดยใช้วิธีการทางไวยากรณ์นี้) จะเป็นvar=1; var=''ตรงกันข้าม

อ้างอิง:

-n = True ถ้าความยาวของสตริง var ไม่ใช่ศูนย์

-z = True ถ้าความยาวของสตริง var เป็นศูนย์


5

ในภาษาการเขียนโปรแกรมหลายประเภทประเภทบูลีนเป็นหรือนำไปใช้เป็นประเภทย่อยของจำนวนเต็มที่trueมีพฤติกรรมเหมือน1และfalseพฤติกรรมเช่น0:

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

# val=1; ((val)) && echo "true" || echo "false"
true
# val=0; ((val)) && echo "true" || echo "false"
false

ผู้ชายทุบตี :

((expression))

นิพจน์ได้รับการประเมินตามกฎที่อธิบายไว้ด้านล่างภายใต้การประเมิน ARITHMETIC หากค่าของนิพจน์ไม่ใช่ศูนย์สถานะการส่งคืนจะเป็น 0 มิฉะนั้นสถานะการส่งคืนคือ 1 ซึ่งเทียบเท่ากับให้ "expression"


5

Bill Parker กำลังลงคะแนนเนื่องจากคำจำกัดความของเขากลับด้านจากการประชุมรหัสปกติ โดยปกติแล้ว true ถูกกำหนดเป็น 0 และ false ถูกกำหนดเป็น nonzero 1 จะทำงานเป็นเท็จเช่นเดียวกับ 9999 และ -1 เช่นเดียวกันกับค่าส่งคืนฟังก์ชั่น - 0 คือความสำเร็จและสิ่งที่ไม่ใช่ศูนย์คือความล้มเหลว ขออภัยฉันยังไม่มีความน่าเชื่อถือบนถนนที่ยังไม่ได้ลงคะแนนหรือตอบกลับเขาโดยตรง

Bash แนะนำให้ใช้วงเล็บสองอันตอนนี้เป็นนิสัยแทนที่จะเป็นวงเล็บเดียวและลิงก์ที่ Mike Holt ให้อธิบายถึงความแตกต่างในการทำงาน 7.3 ผู้ประกอบการเปรียบเทียบอื่น ๆ

สิ่งหนึ่ง-eqคือโอเปอเรเตอร์ตัวเลขดังนั้นจึงต้องมีรหัส

#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

จะออกคำสั่งข้อผิดพลาดโดยคาดหวังว่าการแสดงออกจำนวนเต็ม สิ่งนี้ใช้กับพารามิเตอร์ใดพารามิเตอร์หนึ่งเนื่องจากไม่มีค่าจำนวนเต็ม แต่ถ้าเราใส่วงเล็บสองอันไว้รอบ ๆ มันจะไม่ออกข้อความแสดงข้อผิดพลาด แต่มันจะให้ค่าที่ผิด (ดีใน 50% ของการเปลี่ยนลำดับที่เป็นไปได้) มันจะประเมินเป็น [[0 -eq จริง]] = ความสำเร็จ แต่รวมถึง [[0 -eq false]] = ความสำเร็จซึ่งเป็นสิ่งที่ผิด (hmmm .... สิ่งที่เกี่ยวกับตัวเครื่องนั้นเป็นค่าตัวเลข?)

#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then

มีการเรียงสับเปลี่ยนเงื่อนไขอื่น ๆ ซึ่งจะให้ผลลัพธ์ที่ผิดเช่นกัน โดยพื้นฐานแล้วอะไรก็ตาม (นอกเหนือจากเงื่อนไขข้อผิดพลาดที่ระบุไว้ด้านบน) ที่ตั้งค่าตัวแปรให้เป็นค่าตัวเลขและเปรียบเทียบกับตัวจริง / เท็จในตัวหรือตั้งค่าตัวแปรให้เป็นตัวจริง / เท็จในตัวและเปรียบเทียบกับค่าตัวเลข นอกจากนี้สิ่งที่กำหนดตัวแปรไปในตัวจริง / -eqเท็จและไม่เปรียบเทียบโดย ดังนั้นหลีกเลี่ยง-eqการเปรียบเทียบแบบบูลและหลีกเลี่ยงการใช้ค่าตัวเลขสำหรับการเปรียบเทียบแบบบูล นี่คือบทสรุปของวิธีเรียงสับเปลี่ยนที่จะให้ผลลัพธ์ที่ไม่ถูกต้อง:

# With variable set as an integer and evaluating to true/false
# *** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then

# With variable set as an integer and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then


# With variable set as an true/false builtin and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then

ดังนั้นตอนนี้สิ่งที่ได้ผล ใช้ builtin จริง / เท็จสำหรับทั้งการเปรียบเทียบและการประเมินของคุณ (ตามที่ Mike Hunt จดไว้อย่าใส่ไว้ในเครื่องหมายคำพูด) จากนั้นใช้เครื่องหมายเท่ากับ (= หรือ ==) อย่างใดอย่างหนึ่งหรือสองอย่างและเครื่องหมายวงเล็บเหลี่ยมเดี่ยวหรือคู่ ([] หรือ [[]]) โดยส่วนตัวฉันชอบเครื่องหมายเท่ากับสองเท่าเพราะมันทำให้ฉันนึกถึงการเปรียบเทียบเชิงตรรกะในภาษาการเขียนโปรแกรมอื่น ๆ และเครื่องหมายคำพูดคู่เพียงเพราะฉันชอบพิมพ์ ดังนั้นงานเหล่านี้:

# With variable set as an integer and evaluating to true/false
# *** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then

ที่นั่นคุณมีมัน


2
true/ falsebuilt-ins จะไม่ได้ใช้ที่นี่ (ไม่สนใจสิ่งที่เน้นไวยากรณ์ของบรรณาธิการบางคนอาจจะบอกเป็นนัยว่า) โดยเฉพาะอย่างยิ่งใน[…]กรณีที่คุณสามารถคิดว่ามันเป็นสตริงที่เรียบง่ายที่นี่ (หนึ่งที่จะได้รับเป็นพารามิเตอร์กับ[คำสั่ง)
phk

คุณมีมันตอนนี้
Peter Mortensen

4

ข้อค้นพบและข้อเสนอแนะของฉันแตกต่างจากโพสต์อื่นเล็กน้อย ฉันพบว่าฉันสามารถใช้ "booleans" โดยพื้นฐานแล้วเป็นหนึ่งในภาษา "ปกติ" ใด ๆ โดยไม่แนะนำ "hoop jump" ...

ไม่จำเป็นต้องมี[]หรือเปรียบเทียบสตริงอย่างชัดเจน ... ฉันลองใช้ดิสทริบิวชันหลายตัว ผมทดสอบทุบตี Dash และBusyBox ผลลัพธ์ก็เหมือนกันเสมอ ฉันไม่แน่ใจว่าสิ่งที่โพสต์ที่ได้รับคะแนนสูงสุดดั้งเดิมพูดถึงอะไร บางทีเวลามีการเปลี่ยนแปลงและนั่นคือทั้งหมดที่มีให้มัน?

หากคุณตั้งค่าตัวแปรเป็น trueประเมินว่า "ยืนยัน" ภายในเงื่อนไข ตั้งค่าเป็นfalseและประเมินเป็น "ลบ" ตรงไปตรงมามาก! ข้อแม้เดียวคือตัวแปรที่ไม่ได้กำหนดจะประเมินเหมือนจริง ! มันจะดีถ้ามันไม่ได้ตรงข้าม (มันจะอยู่ในภาษาส่วนใหญ่) แต่ที่เป็นเคล็ดลับ - คุณเพียงแค่ต้องเริ่มต้นอย่างชัดเจน booleans ของคุณให้เป็นจริงหรือเท็จ

ทำไมมันถึงทำงานแบบนี้? คำตอบนั้นคือสองเท่า A) จริง / เท็จในเชลล์จริงๆหมายถึง "ไม่มีข้อผิดพลาด" vs "ข้อผิดพลาด" (เช่น 0 เทียบกับสิ่งอื่นใด) B) จริง / เท็จไม่ใช่ค่า - แต่ค่อนข้างคำสั่งในการเขียนสคริปต์เชลล์! เกี่ยวกับประเด็นที่สองการเรียกใช้งานtrueหรือfalseบนบรรทัดด้วยตัวเองจะตั้งค่าส่งคืนสำหรับบล็อกที่คุณเข้าสู่ค่านั้นนั่นfalseคือการประกาศ "ข้อผิดพลาดที่พบ" โดยที่ "เคลียร์" จริง ใช้กับการกำหนดให้กับตัวแปร "ส่งคืน" ที่เป็นตัวแปร ไม่ได้กำหนดประเมินตัวแปรเช่นtrueในเงื่อนไขเพราะที่แสดงถึงอย่างเท่าเทียมกัน 0 หรือ "ไม่พบข้อผิดพลาด"

ดูตัวอย่างบรรทัด Bash และผลลัพธ์ด้านล่าง ทดสอบด้วยตัวเองหากคุณต้องการยืนยัน ...

#!/bin/sh

# Not yet defined...
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=true
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

myBool=false
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;

อัตราผลตอบแทน

when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false

1

นี่เป็นตัวอย่างง่ายๆที่เหมาะกับฉัน:

temp1=true
temp2=false

if [ "$temp1" = true ] || [ "$temp2" = true ]
then
    echo "Do something." 
else
    echo "Do something else."
fi

1

if trueนี่คือการดำเนินการระยะสั้นส่ง

# Function to test if a variable is set to "true"
_if () {
    [ "${1}" == "true" ] && return 0
    [ "${1}" == "True" ] && return 0
    [ "${1}" == "Yes" ] && return 0
    return 1
}

ตัวอย่างที่ 1

my_boolean=true

_if ${my_boolean} && {
    echo "True Is True"
} || {
    echo "False Is False"
}

ตัวอย่างที่ 2

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"

ใช่การย่อยสลายหน้าที่ไม่ได้รับความนิยม
Peter Mortensen

1

ฉันพบคำตอบที่มีอยู่ทำให้เกิดความสับสน

โดยส่วนตัวฉันแค่อยากมีบางอย่างที่ดูเหมือนและทำงานเหมือน C.

ตัวอย่างนี้ทำงานได้หลายครั้งต่อวันในการผลิต:

snapshotEvents=true

if ($snapshotEvents)
then
    # Do stuff if true
fi

และเพื่อให้ทุกคนมีความสุขฉันได้ทดสอบ:

snapshotEvents=false

if !($snapshotEvents)
then
    # Do stuff if false
fi

ซึ่งยังใช้งานได้ดี

$snapshotEventsตรวจสอบเนื้อหาของค่าของตัวแปร $ดังนั้นคุณจึงจำเป็นต้อง

คุณไม่จำเป็นต้องใช้วงเล็บฉันแค่พบว่ามันมีประโยชน์


2
หากคุณลบวงเล็บนี่คือคำตอบดั้งเดิมของ @ miku ที่ด้านบนทั้งหมด
dolmen

1
หากไม่มีวงเล็บนิพจน์จะไม่ประเมินค่า
จะ

@ จะใช่ใช่ คุณไม่ต้องการ (s)
phil294

1
@Blauhirn ... สวัสดีฉันใช้ความคิดเห็นของฉันในการทดลองกับ GNU Bash บนพีซี Linux Mint / Ubuntu คุณอาจถูกต้องในทางทฤษฎี () - ไม่จำเป็น การตอบสนองของฉันเท่านั้นคือลองดูเหมือนว่าขึ้นอยู่กับรุ่น Bash การแสดงออกหรือบริบทจริงและเช่นนั้น
จะ

1

นี่คือการปรับปรุงคำตอบดั้งเดิมของ miku ที่กล่าวถึงข้อกังวลของDennis Williamsonเกี่ยวกับกรณีที่ตัวแปรไม่ได้ถูกตั้งค่า:

the_world_is_flat=true

if ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

และเพื่อทดสอบว่าตัวแปรคือfalse:

if ! ${the_world_is_flat:-false} ; then
    echo "Be careful not to fall off!"
fi

เกี่ยวกับกรณีอื่น ๆ ที่มีเนื้อหาที่น่ารังเกียจในตัวแปรนี่เป็นปัญหากับอินพุตภายนอกใด ๆ ที่ถูกส่งไปยังโปรแกรม

อินพุตภายนอกใด ๆ จะต้องผ่านการตรวจสอบก่อนที่จะเชื่อถือได้ แต่การตรวจสอบนั้นจะต้องทำเพียงครั้งเดียวเมื่อได้รับอินพุตนั้น

ไม่ต้องส่งผลกระทบต่อประสิทธิภาพการทำงานของโปรแกรมโดยการใช้ตัวแปรทุกครั้งตามที่Dennis Williamsonแนะนำ


1

คุณสามารถใช้shFlags shFlags

มันให้ตัวเลือกในการกำหนด: DEFINE_bool

ตัวอย่าง:

DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");

จากบรรทัดคำสั่งคุณสามารถกำหนด:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False

GFlags ไม่มีเหตุผลในคำตอบนี้ - มันเป็นไลบรารี C ++ ไม่สามารถใช้โดยตรงในเชลล์สคริปต์
Jonathan Cross

อัปเดตการตอบสนองต่อ shFlags ซึ่งเป็นพอร์ตของ GFlags ไปยังเชลล์
gogasca

0

นี่คือการทดสอบความเร็วเกี่ยวกับวิธีต่างๆในการทดสอบค่า "บูลีน" ใน Bash:

#!/bin/bash
rounds=100000

b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done

b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done

b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done

มันจะพิมพ์บางอย่างเช่น

true is a shell builtin
true is /bin/true

real    0m0,815s
user    0m0,767s
sys     0m0,029s

real    0m0,562s
user    0m0,509s
sys     0m0,022s

real    0m0,829s
user    0m0,782s
sys     0m0,008s

real    0m0,782s
user    0m0,730s
sys     0m0,015s

real    0m0,402s
user    0m0,391s
sys     0m0,006s

real    0m0,668s
user    0m0,633s
sys     0m0,008s

real    0m0,344s
user    0m0,311s
sys     0m0,016s

real    0m0,367s
user    0m0,347s
sys     0m0,017s

-2

ทางเลือก - ใช้ฟังก์ชั่น

is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"

การกำหนดฟังก์ชั่นนั้นใช้งานง่ายน้อยลง แต่การตรวจสอบค่าตอบแทนนั้นง่ายมาก


1
นี่ไม่ใช่ตัวแปรที่คุณสามารถทดสอบได้ แต่ฟังก์ชันคงที่
jarno

@jarno การทดสอบค่าส่งคืนของฟังก์ชันแตกต่างจากการทดสอบตัวแปรเพื่อจุดประสงค์ของสคริปต์หรือไม่?
johnraff

คำถามคือเกี่ยวกับตัวแปร
jarno

True แม้ว่าการใช้งานในเชลล์สคริปต์จะเหมือนกัน
johnraff

-2

ทุบตีจริงๆสับสนปัญหากับคนที่ชอบของ[, [[, ((, $((ฯลฯ

ทั้งหมดในพื้นที่โค้ดของกันและกัน ฉันเดาว่านี่เป็นประวัติศาสตร์ส่วนใหญ่ที่ทุบตีต้องแกล้งทำเป็นshบางครั้ง

ส่วนใหญ่ฉันสามารถเลือกวิธีการและติดกับมัน ในกรณีนี้ฉันมักจะประกาศ (โดยเฉพาะอย่างยิ่งในไฟล์ไลบรารีทั่วไปที่ฉันสามารถรวม.ไว้ในสคริปต์จริงของฉัน)

TRUE=1; FALSE=0

ฉันสามารถใช้ตัวดำเนินการทางคณิตศาสตร์((... ))เพื่อทดสอบได้

testvar=$FALSE

if [[ -d ${does_directory_exist} ]]
then
    testvar=$TRUE;
fi

if (( testvar == TRUE )); then
    # Do stuff because the directory does exist
fi
  1. คุณต้องมีระเบียบวินัย คุณtestvarต้องถูกตั้งค่าเป็น$TRUEหรือ$FALSEตลอดเวลา

  2. ใน((... เครื่องมือ))เปรียบเทียบคุณไม่ต้องการสิ่งก่อนหน้า$ซึ่งทำให้อ่านง่ายขึ้น

  3. ฉันสามารถใช้((... ))เพราะ$TRUE=1และ$FALSE=0นั่นคือค่าตัวเลข

  4. ข้อเสียคือต้องใช้เป็น$ครั้งคราว:

    testvar=$TRUE

    ซึ่งไม่ค่อยสวย

มันไม่ได้เป็นโซลูชั่นที่สมบูรณ์แบบ แต่ครอบคลุมทุกกรณีที่ฉันต้องการการทดสอบดังกล่าว


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