สลับตัวแปรบูลีน


26

ฉันต้องการลองใช้สคริปต์อย่างง่าย

flag=false
while !$flag
do
   read x
   if [ "$x" -eq "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

แต่เมื่อฉันเรียกใช้ถ้าฉันพิมพ์trueฉันจะเห็นว่าx="true"และflag="true"แต่รอบไม่สิ้นสุด เกิดอะไรขึ้นกับสคริปต์ ฉันจะสลับตัวแปรบูลีนอย่างถูกต้องได้อย่างไร



ขอบคุณ! ฉันได้รับแล้ว
ผสมพันธุ์

คำตอบ:


21

สคริปต์ของคุณมีข้อผิดพลาดสองข้อ ครั้งแรกคือการที่คุณต้องมีพื้นที่ว่างระหว่าง!และมิฉะนั้นลักษณะเปลือกสำหรับคำสั่งที่เรียกว่า$flag !$flagข้อผิดพลาดที่สองคือ-eqการเปรียบเทียบจำนวนเต็ม แต่คุณใช้มันในสตริง คุณจะเห็นข้อความแสดงข้อผิดพลาดและวนรอบจะดำเนินต่อไปเรื่อย ๆ เนื่องจากเงื่อนไข[ "$x" -eq "true" ]ไม่เป็นจริงหรือค่าที่ไม่ใช่จำนวนเต็มจะถือว่าเป็น 0 และลูปจะออกหากคุณป้อนสตริงใด ๆ (รวมถึงfalse) นอกเหนือจากตัวเลขที่แตกต่างจาก 0

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

flag=false
while [ "$flag" != "true" ]
do
   read x
   if [ "$x" = "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

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

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done

ด้วย[บิวด์อินของkshให้[ x -eq y ]ส่งคืนค่าจริงถ้าxนิพจน์เลขคณิตแก้ไขเป็นจำนวนเดียวกันกับyนิพจน์เชิงคำนวณดังนั้นอินสแตนซ์x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'จะเอาต์พุตใช่
Stéphane Chazelas

10

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

flag= # False
flag=0 # False
flag=1 # True (actually any integer number != 0 will do, but see remark below about toggling)
flag="some string" # Maybe False (make sure that the string isn't interpreted as a number)

if ((flag)) # Test for True
then
  : # do something
fi

if ! ((flag)) # Test for False
then
  : # do something
fi

flag=$((1-flag)) # Toggle flag (only works when flag is either empty or unset, 0, 1, or a string which doesn't represent a number)

ยังใช้งานได้ใน ksh ฉันจะไม่แปลกใจถ้ามันจะทำงานในเชลล์ที่รองรับ POSIX ทั้งหมด แต่ไม่ได้ตรวจสอบมาตรฐาน


1
ไม่! ! ((val))ทำงานในทุบตีเช่นใน C หรือ C ++? ตัวอย่างเช่นถ้าvalเป็น 0 มันก็ยังคงเป็น 0 ! !หลังจาก ถ้าvalเป็น 1 มันยังคงอยู่ 1 ! !หลัง ถ้าvalเป็น 8 มันจะถูกดึงลงมาที่ 1 หลังจาก! !นั้น หรือฉันต้องถามคำถามอื่น?

1
-1 | ใน POSIX sh สแตนด์อะโลน ((.. )) จะไม่ถูกกำหนด โปรดลบการอ้างอิงถึงมัน
LinuxSecurityFreak

Vlastimil คำถามเฉพาะเกี่ยวกับทุบตี - ไม่ใช่เปลือก posix ทำไมต้องลงคะแนนเมื่อคำถามไม่เกี่ยวกับหัวข้อนั้น
Nick Bull

6

หากคุณมั่นใจได้อย่างแน่นอนว่าตัวแปรนั้นจะมีค่า 0 หรือ 1 คุณสามารถใช้ตัวดำเนินการ XOR-bitwise เพื่อพลิกระหว่างสองค่า:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0

2

สิ่งนี้ใช้ได้กับฉัน:

flag=false
while [[ "$flag" == "false" ]]
do
   read x
   if [[ "$x" == "true" ]]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

แต่จริงๆแล้วสิ่งที่คุณควรทำคือแทนที่whileด้วย:

while ! $flag

0

ไม่มีแนวคิดของตัวแปรบูลีนในเชลล์
ตัวแปรเชลล์เท่านั้นอาจจะtext(สตริง) และในบางกรณีข้อความที่อาจถูกตีความว่าเป็นจำนวนเต็ม ( 1, 0xa, 010ฯลฯ )

ดังนั้นการสื่อความflag=trueหมายจึงไม่มีความจริงหรือความผิดพลาดต่อเชลล์เลย

เชือก

สิ่งที่สามารถทำได้คือการเปรียบเทียบสตริง[ "$flag" == "true" ]หรือใช้เนื้อหาตัวแปรในบางคำสั่งและตรวจสอบผลของมันเช่นดำเนินการtrue(เพราะมีทั้งเรียกใช้งานได้เรียกtrueและหนึ่งเรียกว่าfalse) เป็นคำสั่งและตรวจสอบว่ารหัสทางออกของคำสั่งนั้นเป็นศูนย์ (ที่ประสบความสำเร็จ)

$flag; if [ "$?" -eq 0 ]; then ... fi

หรือสั้นกว่า:

if "$flag"; then ... fi

หากเนื้อหาของตัวแปรถูกใช้เป็นคำสั่ง a !สามารถใช้เพื่อลบล้างสถานะการออกของคำสั่งหากมีช่องว่างระหว่างทั้งสอง ( ! cmd) ดังใน:

if ! "$flag"; then ... fi

สคริปต์ควรเปลี่ยนเป็น:

flag=false
while ! "$flag" 
do
   read x
   if [ "$x" == "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

จำนวนเต็ม

ใช้ค่าตัวเลขและเลขคณิตขยาย

ในกรณีนี้รหัสทางออกของ$((0))เป็น1และรหัสทางออกของมี$((1)) ใน bash, ksh และ zsh สามารถดำเนินการทางคณิตศาสตร์ภายใน(โปรดทราบว่าการเริ่มต้นหายไป)0
((..))$

flag=0; if ((flag)); then ... fi

รหัสพกพารุ่นนี้มีความซับซ้อนมากขึ้น:

flag=0; if [ "$((flag))" -eq 0 ]; then ... fi      # test for a number
flag=0; if [ "$((flag))"  == 0 ]; then ... fi      # test for the string "0"

ใน bash / ksh / zsh คุณสามารถทำได้:

flag=0    
while ((!flag)) 
do
   read x
   [ "$x" == "true" ] && flag=1
   echo "${x} : ${flag}"
done

อีกทางเลือกหนึ่ง

คุณสามารถ "สลับตัวแปรบูลีน" (โดยมีค่าเป็นตัวเลข) ดังนี้:

((flag=!flag))

ที่จะเปลี่ยนค่าของflagอย่างใดอย่างหนึ่งหรือ01


หมายเหตุ : โปรดตรวจสอบข้อผิดพลาดในhttps://www.shellcheck.net/ก่อนที่จะโพสต์รหัสของคุณเป็นคำถามหลายครั้งที่เพียงพอที่จะพบปัญหา


0

untilสั้นสำหรับwhile !(และuntilตรงกันข้ามกับ!Bourne) ดังนั้นคุณสามารถทำได้:

flag=false
until "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

ซึ่งควรเหมือนกับ:

flag=false
while ! "$flag"
do
   IFS= read -r x
   if [ "$x" = true ]
   then
     flag=true
   fi
   printf '%s\n' "$x : $flag"
done

คุณสามารถเขียนมันเป็น:

until
   IFS= read -r x
   printf '%s\n' "$x"
   [ "$x" = true ]
do
   continue
done

ส่วนระหว่างuntil/ whileและdoไม่จำเป็นต้องเป็นคำสั่งเดียว

!เป็นคำหลักในไวยากรณ์เชลล์ POSIX จะต้องมีการคั่น, โทเค็นแยกออกมาจากสิ่งต่อไปนี้และเป็นสัญญาณแรกที่นำท่อ ( ! foo | barขัดแย้งท่อและfoo | ! barไม่ถูกต้องแม้ว่าเปลือกหอยบางส่วนยอมรับว่ามันเป็นส่วนขยาย)

!trueถูกตีความว่าเป็น!trueคำสั่งที่ไม่น่าจะมีอยู่จริง ! trueควรทำงาน. !(true)ควรจะทำงานในเปลือกหอย POSIX สอดคล้องกับที่!คั่นที่มันตามมาด้วย(โทเค็น แต่ในทางปฏิบัติมันไม่ได้ทำงานksh/ bash -O extglobที่มันขัดแย้งกับ!(pattern)ผู้ประกอบการ glob ขยายมิได้ zsh (ยกเว้นในการดวลจุดโทษจำลอง) ซึ่งขัดแย้งกับglob(qualifiers)กับbareglobqualและglob(group)ไม่มี


-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

หรือแม้กระทั่ง:

[ ${FOO:-false} == false ] && FOO=true || FOO=false

ควรทำงาน

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