Đảo ngược biến boolean


26

Tôi muốn thử kịch bản đơn giản

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

Nhưng khi tôi chạy nó, nếu tôi gõ true, tôi sẽ thấy điều đó x="true"flag="true", nhưng chu trình không kết thúc. Điều gì là sai với kịch bản? Làm thế nào tôi có thể đảo ngược một biến boolean đúng cách?



cám ơn! tôi hiểu rồi
sinh

Câu trả lời:


21

Có hai lỗi trong kịch bản của bạn. Đầu tiên là bạn cần một khoảng trắng giữa !$flag, nếu không, shell tìm kiếm một lệnh được gọi !$flag. Lỗi thứ hai là -eqso sánh số nguyên, nhưng bạn đang sử dụng nó trên một chuỗi. Tùy thuộc vào vỏ của bạn, bạn sẽ thấy thông báo lỗi và vòng lặp sẽ tiếp tục mãi vì điều kiện [ "$x" -eq "true" ]không thể đúng hoặc mọi giá trị không nguyên sẽ được coi là 0 và vòng lặp sẽ thoát nếu bạn nhập bất kỳ chuỗi nào (bao gồm false) khác với một số khác 0.

Trong khi đó ! $flaglà chính xác, đó là một ý tưởng tồi để coi một chuỗi như là một lệnh. Nó sẽ hoạt động, nhưng nó sẽ rất nhạy cảm với những thay đổi trong tập lệnh của bạn, vì bạn cần đảm bảo rằng $flagkhông bao giờ có thể là bất cứ điều gì ngoài truehoặc false. Sẽ tốt hơn nếu sử dụng so sánh chuỗi ở đây, như trong thử nghiệm dưới đây.

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

Có lẽ có một cách tốt hơn để thể hiện logic bạn đang theo đuổi. Ví dụ, bạn có thể tạo một vòng lặp vô hạn và phá vỡ nó khi bạn phát hiện điều kiện kết thúc.

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

Với phần [dựng sẵn của ksh, [ x -eq y ]trả về true nếu xbiểu thức số học giải quyết cùng số với ybiểu thức số học, ví dụ như x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'vậy sẽ xuất ra có.
Stéphane Chazelas

10

Mặc dù không có biến Boolean trong Bash, nhưng rất dễ dàng mô phỏng chúng bằng cách sử dụng đánh giá số học.

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)

Điều này cũng hoạt động trong ksh. Tôi sẽ không ngạc nhiên nếu nó hoạt động trong tất cả các hệ vỏ tương thích POSIX, nhưng chưa kiểm tra tiêu chuẩn.


1
! ! ((val))hoạt động trong Bash, như trong C hoặc C ++ không? Ví dụ: nếu vallà 0 thì nó vẫn là 0 sau ! !. Nếu vallà 1 thì vẫn là 1 sau ! !. Nếu vallà 8 thì được kéo xuống 1 sau ! !. Hay tôi cần hỏi một câu hỏi khác?

1
-1 | Trong POSIX sh, độc lập ((..)) không được xác định; vui lòng xóa tham chiếu đến nó
LinuxSecurityFreak

Vlastimil câu hỏi cụ thể là về bash - không phải vỏ posix. Tại sao lại bỏ phiếu khi câu hỏi không liên quan đến chủ đề đó?
Nick Bull

6

Nếu bạn có thể hoàn toàn chắc chắn rằng biến đó sẽ chứa 0 hoặc 1, bạn có thể sử dụng toán tử XOR bằng bit để lật giữa hai giá trị:

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

2

Điều này làm việc cho tôi:

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

Nhưng, thực sự, những gì bạn nên làm là thay thế whilebằng:

while ! $flag

0

Không có khái niệm về một biến boolean trong shell.
Các biến Shell chỉ có thể là text(một chuỗi) và, trong một số trường hợp, văn bản đó có thể được hiểu là một số nguyên ( 1,0xa , 010, vv).

Do đó, một flag=true ngụ ý không có sự trung thực hoặc giả dối cho vỏ.

Chuỗi

Những gì có thể được thực hiện là so sánh chuỗi [ "$flag" == "true" ]hoặc sử dụng nội dung biến trong một số lệnh và kiểm tra hậu quả của nó , như thực thi true(vì có cả lệnh thực thi được gọi truefalselệnh được gọi ) như một lệnh và kiểm tra xem mã thoát của lệnh đó có bằng không (thành công).

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

Hoặc ngắn hơn:

if "$flag"; then ... fi

Nếu nội dung của một biến được sử dụng như một lệnh, thì !có thể được sử dụng để phủ định trạng thái thoát của lệnh, nếu một khoảng trắng tồn tại giữa cả hai ( ! cmd), như trong:

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

Kịch bản sẽ thay đổi thành:

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

Số nguyên

Sử dụng các giá trị số và mở rộng số học .

Trong trường hợp này, mã thoát $((0))1và mã thoát $((1))0.
Trong bash, ksh và zsh số học có thể được thực hiện bên trong một ((..))(lưu ý rằng bắt đầu $bị thiếu).

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

Một phiên bản di động của mã này là phức tạp hơn:

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

Trong bash / ksh / zsh bạn có thể làm:

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

Hoặc

Bạn có thể "Đảo ngược một biến boolean" (miễn là nó chứa một giá trị số) là:

((flag=!flag))

Điều đó sẽ thay đổi giá trị của flagmột trong hai 0hoặc 1.


Lưu ý : Vui lòng kiểm tra lỗi trong https://www.shellcheck.net/ trước khi đăng mã của bạn dưới dạng câu hỏi, nhiều lần là đủ để tìm ra vấn đề.


0

untillà viết tắt của while !(và until, trái với !Bourne), vì vậy bạn có thể làm:

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

Mà nên giống như:

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

Bạn cũng có thể viết nó như sau:

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

Phần giữa until/ whiledokhông phải là một lệnh đơn.

!là một từ khóa trong cú pháp shell POSIX. Nó cần được phân định, một mã thông báo tách biệt với mã thông báo tiếp theo và là mã thông báo đầu tiên đi trước một đường ống ( ! foo | barphủ định đường ống và foo | ! barkhông hợp lệ mặc dù một số shell chấp nhận nó như một phần mở rộng).

!trueđược hiểu là !truelệnh không có khả năng tồn tại. ! truenên làm việc. !(true)nên hoạt động trong các vỏ tuân thủ POSIX vì !được phân định bởi (mã được theo sau bởi mã thông báo, nhưng trên thực tế, nó không hoạt động trong ksh/ bash -O extglobnơi nó xung đột với !(pattern)toán tử toàn cầu mở rộng cũng như zsh (ngoại trừ trong mô phỏng sh) khi nó xung đột glob(qualifiers)với bareglobqualglob(group)không có.


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

hoặc thậm chí:

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

nên làm công việc

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.