Balikkan variabel boolean

26

Saya ingin mencoba skrip sederhana

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

Tetapi ketika saya menjalankannya, jika saya mengetik true, saya akan melihat itu x="true"dan flag="true", tetapi siklus tidak berakhir. Apa yang salah dengan skrip? Bagaimana saya bisa membalikkan variabel boolean dengan benar?

trah
sumber
Terima kasih! saya mengerti sekarang
breedish

Jawaban:

21

Ada dua kesalahan dalam skrip Anda. Yang pertama adalah bahwa Anda memerlukan ruang di antara !dan $flag, jika tidak, shell mencari perintah yang disebut !$flag. Kesalahan kedua -eqadalah untuk perbandingan integer, tetapi Anda menggunakannya pada string. Bergantung pada shell Anda, Anda akan melihat pesan kesalahan dan loop akan berlanjut selamanya karena kondisinya [ "$x" -eq "true" ]tidak benar, atau setiap nilai non-integer akan diperlakukan sebagai 0 dan loop akan keluar jika Anda memasukkan string apa pun (termasuk false) selain angka yang berbeda dari 0.

Meskipun ! $flagbenar, itu ide yang buruk untuk memperlakukan string sebagai perintah. Ini akan berhasil, tetapi akan sangat sensitif terhadap perubahan pada skrip Anda, karena Anda harus memastikan bahwa itu $flagtidak akan menjadi apa pun kecuali trueatau false. Akan lebih baik menggunakan perbandingan string di sini, seperti dalam tes di bawah ini.

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

Mungkin ada cara yang lebih baik untuk mengekspresikan logika yang Anda cari. Misalnya, Anda bisa membuat loop tak terbatas dan mematahkannya ketika Anda mendeteksi kondisi terminasi.

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Dengan [builtin of ksh, [ x -eq y ]mengembalikan true jika xekspresi aritmatika diselesaikan ke nomor yang sama dengan yekspresi aritmatika jadi misalnya x=2 true=1+1 ksh -c '[ x -eq true ] && echo yes'akan menampilkan ya.
Stéphane Chazelas
10

Meskipun tidak ada variabel Boolean di Bash, sangat mudah untuk meniru mereka menggunakan evaluasi aritmatika.

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)

Ini juga berfungsi di ksh. Saya tidak akan terkejut jika bekerja di semua shell yang sesuai dengan POSIX, tetapi belum memeriksa standarnya.

ack
sumber
1
Apakah ! ! ((val))berfungsi di Bash, seperti di C atau C ++? Misalnya, jika val0 tetap 0 setelah ! !. Jika val1 tetap 1 setelah ! !. Jika val8 maka ditarik ke 1 setelah ! !. Atau apakah saya perlu mengajukan pertanyaan lain?
1
-1 | Dalam POSIX sh, standalone ((..)) tidak terdefinisi; silakan hapus referensi untuk itu
LinuxSecurityFreak
Vlastimil pertanyaannya secara khusus adalah tentang bash - bukan posix shell. Mengapa memilih ketika pertanyaannya bukan tentang topik itu?
Nick Bull
6

Jika Anda dapat benar-benar yakin bahwa variabel tersebut akan berisi 0 atau 1, Anda dapat menggunakan operator bitwise XOR untuk membalik di antara dua nilai:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0
pengguna971217
sumber
2

Ini bekerja untuk saya:

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

Tapi, sebenarnya, yang harus Anda lakukan adalah mengganti whiledengan:

while ! $flag
ДМИТРИЙ МАЛИКОВ
sumber
0

Tidak ada konsep variabel boolean di shell.
Variabel shell hanya bisa text(string), dan, dalam beberapa kasus, teks yang dapat ditafsirkan sebagai integer ( 1, 0xa, 010, dll).

Karena itu, a flag=truetidak menyiratkan kebenaran atau kepalsuan pada cangkang sama sekali.

Tali

Apa yang bisa dilakukan adalah perbandingan string [ "$flag" == "true" ]atau menggunakan konten variabel dalam beberapa perintah dan memeriksa konsekuensinya , seperti mengeksekusi true(karena ada keduanya dieksekusi dipanggil truedan satu dipanggil false) sebagai perintah dan periksa apakah kode keluar dari perintah itu nol (berhasil).

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

Atau lebih pendek:

if "$flag"; then ... fi

Jika konten variabel digunakan sebagai perintah, a !dapat digunakan untuk meniadakan status keluar dari perintah, jika ada spasi di antara keduanya ( ! cmd), seperti pada:

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

Skrip harus berubah menjadi:

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

Bilangan bulat

Gunakan nilai numerik dan Ekspansi Aritmatika .

Dalam hal ini, kode keluar $((0))is 1dan kode exit $((1))is 0.
Dalam bash, ksh dan zsh aritmatika dapat dilakukan di dalam ((..))(perhatikan bahwa permulaan $hilang).

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

Versi portabel dari kode ini lebih berbelit-belit:

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

Di bash / ksh / zsh Anda bisa melakukan:

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

kalau tidak

Anda dapat "Membalikkan variabel boolean" (asalkan berisi nilai numerik) sebagai:

((flag=!flag))

Itu akan mengubah nilai flagmenjadi 0atau 1.


Catatan : Silakan periksa kesalahan di https://www.shellcheck.net/ sebelum memposting kode Anda sebagai pertanyaan, berkali-kali itu sudah cukup untuk menemukan masalah.

Ishak
sumber
0

untilkependekan dari while !(dan until, berlawanan dengan !Bourne), sehingga Anda dapat melakukan:

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

Yang harus sama dengan:

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

Anda juga dapat menuliskannya sebagai:

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

Bagian antara until/ whiledan dotidak harus menjadi satu perintah.

!adalah kata kunci dalam sintaks POSIX shell. Ini perlu dibatasi, token yang terpisah dari yang mengikuti dan menjadi token pertama yang mendahului pipa ( ! foo | barmeniadakan pipa dan foo | ! bartidak valid meskipun beberapa shell menerimanya sebagai perpanjangan).

!trueditafsirkan sebagai !trueperintah yang tidak mungkin ada. ! trueharus bekerja. !(true)harus bekerja di shell yang sesuai dengan POSIX seperti yang !dibatasi karena diikuti oleh (token, tetapi dalam praktiknya ia tidak bekerja di ksh/ di bash -O extglobmana ia berkonflik dengan !(pattern)operator glob yang diperluas atau zsh (kecuali dalam emulasi sh) di mana ia berkonflik glob(qualifiers)dengan bareglobqualdan glob(group)tanpa.

Stéphane Chazelas
sumber
-1
[ ${FOO:-0} == 0 ] && FOO=1 || FOO=0

atau bahkan:

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

harus melakukan pekerjaan

Slaxor
sumber