Bagaimana saya bisa mendeklarasikan dan menggunakan variabel Boolean dalam skrip shell?

980

Saya mencoba mendeklarasikan variabel Boolean dalam skrip shell menggunakan sintaks berikut:

variable=$false

variable=$true

Apakah ini benar? Juga, jika saya ingin memperbarui variabel itu apakah saya akan menggunakan sintaks yang sama? Akhirnya, apakah sintaks berikut untuk menggunakan variabel Boolean sebagai ekspresi benar?

if [ $variable ]

if [ !$variable ]
hassaanm
sumber
73
WASPADALAH! truedan falsedalam konteks yang paling potongan di bawah ini hanya string polos, tidak yang bash built-ins!!! Silakan baca jawaban Mike Holt di bawah ini. (Ini adalah salah satu contoh di mana jawaban yang sangat dipilih dan diterima adalah IMHO membingungkan dan membayangi konten wawasan dalam jawaban yang lebih rendah)
mjv
7
@mjv Sebagian besar kebingungan atas pertanyaan ini (dan jawaban Miku) disebabkan oleh fakta bahwa Miku merevisi jawabannya di beberapa titik setelah beberapa komentar diposting yang menggambarkan bagaimana jawaban Miku melibatkan memanggil bash built-in true. Ternyata jawaban asli Miku benar-benar memanggil truebuilt-in, tetapi jawaban yang direvisi tidak. Hal ini menyebabkan komentar tersebut keliru tentang bagaimana kode Miku bekerja. Jawaban Miku sejak itu telah diedit untuk secara eksplisit menunjukkan kode asli dan revisi. Semoga ini menempatkan kebingungan untuk beristirahat sekali dan untuk semua.
Mike Holt
2
[ true ] && echo yes, true is truedan (upppsss) [ false ] && echo yes, false is also true. / bin / true dan / bin / false memberikan kode pengembalian $? untuk fungsi bukan untuk perbandingan.
fcm
Jika var diatur variable=somethingdaripada yang benar, jika tidak disetel variable=itu akan menjadi salah [[ $variable ]] && echo true || echo falsedan terbalik[[ ! $variable ]] && echo false || echo true
Ivan

Jawaban:

1202

Jawaban yang Direvisi (12 Feb 2014)

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

Jawaban Asli

Peringatan: 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

Dari: Menggunakan variabel boolean di Bash

Alasan jawaban asli dimasukkan di sini adalah karena komentar sebelum revisi pada 12 Februari 2014 hanya berkaitan dengan jawaban asli, dan banyak komentar salah ketika dikaitkan dengan jawaban yang direvisi. Misalnya, komentar Dennis Williamson tentang bash builtin truepada 2 Juni 2010 hanya berlaku untuk jawaban asli, bukan yang direvisi.

miku
sumber
37
Untuk menjelaskan apa yang terjadi: ifpernyataan mengeksekusi isi variabel yang merupakan Bash builtin true. Perintah apa pun dapat ditetapkan sebagai nilai variabel dan nilai keluarnya akan dievaluasi.
Dijeda sampai pemberitahuan lebih lanjut.
7
@ pms Operator "-o" dan "-a" hanya untuk perintah "test" (alias "[]"). Sebaliknya, ini adalah "jika + perintah", tanpa "tes". (Seperti "if grep foo file; then ...".) Jadi, gunakan yang normal &&dan ||operator: # t1=true; t2=true; f1=false;# if $t1 || $f1; then echo is_true ; else echo is_false; fi; (mengembalikan "true", karena t1 = true) # if $t1 && $f1 || $t2; then echo is_true ; else echo is_false; fi (mengembalikan "true", karena t2 = true) . Sekali lagi, ini HANYA berfungsi karena "true" / "false" adalah bash-builtin (menghasilkan true / false). Anda tidak dapat menggunakan "jika $ var ..." kecuali var adalah cmd (yaitu, benar atau salah)
michael
14
-1, lihat jawaban saya untuk penjelasan.
Dennis
3
Banyak informasi yang salah, di sini. / bin / true tidak digunakan secara efektif. Lihat jawaban Dennis.
ajk
1
Kode ini tidak sama dan tidak berfungsi dengan cara yang sama seperti artikel yang ditautkan. Kode yang ditautkan memanggil program dengan nama yang disimpan dalam variabel tetapi kode dalam jawaban ini hanyalah perbandingan string.
Pertanyaan Quolonel
794

TL; DR

bool=true

if [ "$bool" = true ]

Masalah dengan jawaban ( asli ) Miku

Saya tidak merekomendasikan jawaban yang diterima 1 . Sintaksnya cukup, tetapi memiliki beberapa kekurangan.

Katakanlah kita memiliki kondisi berikut.

if $var; then
  echo 'Muahahaha!'
fi

Dalam kasus-kasus 2 berikut , kondisi ini akan mengevaluasi ke true dan menjalankan perintah bersarang.

# 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

Biasanya Anda hanya ingin kondisi Anda dievaluasi menjadi true ketika variabel "Boolean" Anda, vardalam contoh ini, secara eksplisit disetel ke true. Semua kasus lainnya sangat menyesatkan!

Kasus terakhir (# 5) sangat nakal karena akan mengeksekusi perintah yang terkandung dalam variabel (itulah sebabnya kondisi mengevaluasi true untuk perintah yang valid 3, 4 ).

Ini adalah contoh yang tidak berbahaya:

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!

Mengutip variabel Anda lebih aman, misalnya if "$var"; then. Dalam kasus di atas, Anda harus mendapatkan peringatan bahwa perintah tidak ditemukan. Tapi kami masih bisa berbuat lebih baik (lihat rekomendasi saya di bagian bawah)

Lihat juga penjelasan Mike Holt tentang jawaban asli Miku.

Masalah dengan jawaban Hbar

Pendekatan ini juga memiliki perilaku yang tidak terduga.

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

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

Anda akan mengharapkan kondisi di atas untuk mengevaluasi ke false, sehingga tidak pernah menjalankan pernyataan bersarang. Mengherankan!

Mengutip nilai ( "false"), mengutip variabel ( "$var"), atau menggunakan testatau [[bukannya [, tidak membuat perbedaan.

Apa yang saya lakukan merekomendasikan:

Berikut adalah cara yang saya sarankan Anda memeriksa "Boolean" Anda. Mereka bekerja seperti yang diharapkan.

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

Semuanya setara. Anda harus mengetik beberapa kali penekanan tombol daripada pendekatan di jawaban 5 lainnya , tetapi kode Anda akan lebih defensif.


Catatan kaki

  1. Jawaban Miku sejak itu telah diedit dan tidak lagi mengandung kekurangan (dikenal).
  2. Bukan daftar lengkap.
  3. Perintah yang valid dalam konteks ini berarti perintah yang ada. Tidak masalah jika perintah digunakan dengan benar atau salah. Misalnya man womanmasih akan dianggap sebagai perintah yang valid, bahkan jika tidak ada halaman manual tersebut.
  4. Untuk perintah yang tidak valid (tidak ada), Bash hanya akan mengeluh bahwa perintah itu tidak ditemukan.
  5. Jika Anda peduli panjang, rekomendasi pertama adalah yang terpendek.
Dennis
sumber
8
Menggunakan ==dengan [atau testtidak portabel. Mengingat portabilitas adalah satu-satunya keuntungan [/ testmemiliki lebih [[, tongkat dengan =.
chepner
2
@Scott Saya menggunakan ikan sebagai shell utama saya, yang memiliki bahasa scripting yang waras dibandingkan dengan bash menurut pendapat saya.
Dennis
1
Ya, saya tidak bisa menemukan komentar apa pun untuk lelucon tersembunyi ini, jadi harus tunjukkan =)
Kranach
5
Bagi saya, secara konseptual lebih mudah dipahami jika saya menggunakan bool = "true". Maka jelas bahwa itu hanya string dan bukan nilai atau builtin tertentu.
wisbucky
1
@dolmen benar-benar, mengevaluasi input tidak berisiko ketika Anda mengendalikan input, tapi saya masih menganggapnya sebagai praktik buruk yang harus dihindari jika dapat dengan mudah dihindari. Seseorang yang hanya pernah melihat dan menggunakan gaya sebelumnya mungkin tidak tahu tentang kekurangannya yang dapat menyebabkan perilaku yang tidak terduga.
Dennis
175

Tampaknya ada beberapa kesalahpahaman di sini tentang Bash builtin true, dan lebih khusus lagi, tentang bagaimana Bash memperluas dan menafsirkan ekspresi di dalam tanda kurung.

Kode dalam jawaban miku sama sekali tidak ada hubungannya dengan Bash builtin true, atau /bin/true, atau rasa trueperintah lainnya. Dalam hal ini, truetidak lebih dari string karakter sederhana, dan tidak ada panggilan ke trueperintah / builtin yang pernah dibuat, baik oleh penugasan variabel, maupun dengan evaluasi ekspresi kondisional.

Kode berikut secara fungsional identik dengan kode dalam jawaban miku:

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

Satu- satunya perbedaan di sini adalah bahwa empat karakter yang dibandingkan adalah 'y', 'e', ​​'a', dan 'h' bukannya 't', 'r', 'u', dan 'e'. Itu dia. Tidak ada upaya yang dilakukan untuk memanggil perintah atau builtin bernama yeah, juga tidak ada (dalam contoh miku) segala jenis penanganan khusus terjadi ketika Bash mem-parsing token true. Itu hanya string, dan sepenuhnya arbitrer.

Pembaruan (2014-02-19): Setelah mengikuti tautan dalam jawaban miku, sekarang saya melihat dari mana beberapa kebingungan itu berasal. Jawaban Miku menggunakan tanda kurung tunggal, tetapi cuplikan kode yang ditautkannya tidak menggunakan tanda kurung. Hanya saja:

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

Kedua potongan kode akan berperilaku dengan cara yang sama, tetapi tanda kurung sepenuhnya mengubah apa yang terjadi di bawah tenda.

Inilah yang dilakukan Bash dalam setiap kasus:

Tidak ada tanda kurung:

  1. Perluas variabel $the_world_is_flatke string "true".
  2. Coba parsing string "true"sebagai perintah.
  3. Temukan dan jalankan trueperintah (baik builtin atau /bin/true, tergantung pada versi Bash).
  4. Bandingkan kode keluar dari trueperintah (yang selalu 0) dengan 0. Ingat bahwa di sebagian besar shell, kode keluar 0 menunjukkan keberhasilan dan yang lainnya menunjukkan kegagalan.
  5. Karena kode keluar adalah 0 (berhasil), jalankan klausa ifpernyataan ituthen

Kurung:

  1. Perluas variabel $the_world_is_flatke string "true".
  2. Mengurai ekspresi bersyarat yang sekarang sepenuhnya diperluas, yang merupakan bentuk string1 = string2. The =operator adalah bash perbandingan string operator. Begitu...
  3. Lakukan perbandingan string pada "true"dan "true".
  4. Yap, kedua string itu sama, sehingga nilai kondisionalnya benar.
  5. Jalankan klausa ifpernyataan itu then.

Kode tanpa kurung berfungsi, karena trueperintah mengembalikan kode keluar 0, yang menunjukkan keberhasilan. Kode kurung berfungsi, karena nilai $the_world_is_flatidentik dengan string literal truedi sisi kanan =.

Hanya untuk mengarahkan titik awal, pertimbangkan dua cuplikan kode berikut:

Kode ini (jika dijalankan dengan hak akses root) akan me-reboot komputer Anda:

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

Kode ini hanya mencetak "Usaha yang bagus." Perintah reboot tidak dipanggil.

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

Pembaruan (2014-04-14) Untuk menjawab pertanyaan dalam komentar mengenai perbedaan antara =dan ==: AFAIK, tidak ada perbedaan. The ==operator adalah sinonim Bash-spesifik untuk =, dan sejauh yang saya telah melihat, mereka bekerja persis sama dalam semua konteks.

Catatan, bagaimanapun, bahwa aku secara khusus berbicara tentang =dan ==operator perbandingan string digunakan baik [ ]atau [[ ]]tes. Saya tidak menyarankan itu =dan ==dapat dipertukarkan di mana-mana di bash.

Misalnya, Anda jelas tidak dapat melakukan penugasan variabel ==, seperti var=="foo"(baik secara teknis Anda dapat melakukan ini, tetapi nilainya varakan "=foo", karena Bash tidak melihat ==operator di sini, itu melihat =operator (penugasan), diikuti oleh nilai literal ="foo", yang baru saja menjadi "=foo").

Juga, meskipun =dan ==dapat dipertukarkan, Anda harus ingat bahwa cara kerja tes-tes itu bergantung pada apakah Anda menggunakannya di dalam [ ]atau [[ ]], dan juga pada apakah operan dikutip atau tidak. Anda dapat membaca lebih lanjut tentang itu di Panduan Skrip Bash Lanjutan: 7.3 Operator Perbandingan Lainnya (gulir ke bawah ke diskusi tentang =dan ==).

Mike Holt
sumber
Pendekatan tanpa kurung juga memiliki keuntungan membiarkan Anda menulis satu baris yang bersih, jelas (imo) seperti$the_world_is_flat && echo "you are in flatland!"
ajk
9
Benar. Meskipun, saya tidak menganjurkan untuk (atau menentang) pendekatan baik. Saya hanya ingin menjernihkan beberapa informasi yang salah yang dipilih di sini, sehingga orang-orang yang menemukan topik ini nantinya tidak akan pergi dengan banyak kesalahpahaman tentang bagaimana semua ini bekerja.
Mike Holt
1
Alasan kebingungan adalah bahwa jawaban asli miku berdiri selama 4 tahun. Semua referensi ke builtin truedibuat mengenai jawaban asli. (Jawaban yang direvisi pada 12 Februari 2014 tidak diajukan oleh miku.) Saya telah mengedit jawaban untuk menyertakan yang asli dan yang telah direvisi. Kemudian komentar orang masuk akal.
wisbucky
1
Dari membaca jawaban yang ditawarkan di sini, saya mendapat kesan bahwa tidak ada yang benar-benar menggunakan yang asli true. Apakah ada cara? Saya menduga banyak programmer yang terbiasa dengan bahasa yang lebih ketat untuk melihat jawaban ini untuk membantu mereka mencampurkan beberapa bashlem untuk membuat hidup mereka sedikit lebih mudah akan membutuhkan ===operator sehingga string dan "boolean" sebenarnya tidak dapat dipertukarkan. Haruskah mereka hanya menempel 0 dan 1 dan penggunaan (( $maybeIAmTrue ))seperti yang disarankan di Quolonel Pertanyaan 's jawabannya ?
SeldomNeedy
2
Untuk membahas komentar SeldomNeedy, ya, Anda dapat menggunakan yang asli true, tetapi umumnya bukan sebagai sesuatu untuk membandingkan variabel dengan, karena yang sebenarnya truetidak memiliki nilai per se. Yang dilakukannya hanyalah mengatur status keluar 0, yang menunjukkan keberhasilan. Perlu dicatat bahwa itu pada dasarnya setara dengan apa yang disebut "perintah nol", atau :. Sejauh menggunakan 0dan 1, itulah yang saya lakukan di semua skrip saya hari ini di mana saya membutuhkan boolean. Dan saya menggunakan (( ))operator alih-alih [[ ]]mengevaluasi. Jadi, misalnya, jika saya punya flag=0, saya bisa melakukannyaif (( flag )); then ...
Mike Holt
57

Gunakan ekspresi aritmatika.

#!/bin/bash

false=0
true=1

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

Keluaran:

benar
bukan salah

Pertanyaan Quolonel
sumber
3
pro: (1.) perilaku mirip dengan cara C menangani bools, (2.) sintaksisnya sangat ringkas / minimal (tidak memerlukan variabel tangan kanan dan operator seperti '=' atau '=='), (3 .) <subjektif> bagi saya, saya mengerti apa yang terjadi tanpa penjelasan panjang lebar ... kontras dengan jawaban Miku dan Dennis yang keduanya tampaknya memerlukan penjelasan panjang lebar </subjektif>
Trevor Boyd Smith
3
@ TrevorBoydSmith Mengapa Anda tidak mengatakan, "pro: segalanya, kontra: tidak ada". Akan menghemat biaya penyusutan pada keyboard dan monitor Anda dalam jangka panjang.
Pertanyaan Quolonel
4
Untuk penggunaan interaktif, seperti one-liner, pastikan untuk meninggalkan spasi setelahnya !, atau akan melakukan ekspansi sejarah. ((! foo))bekerja, begitu juga ! ((foo)). Saya suka solusi ini, BTW. Akhirnya cara ringkas untuk melakukan variabel boolean. ((foo || bar))bekerja seperti yang diharapkan.
Peter Cordes
5
(())memperluas variabel secara rekursif, yang tidak saya harapkan. foo=bar; bar=baz; ((foo)) && echo echotidak mencetak apa pun, tetapi itu benar adanya baz=1. Jadi, Anda dapat mendukung foo=truedan foo=falsejuga 0 atau 1 dengan melakukan true=1.
Peter Cordes
2
@quolonel terima kasih atas sumber yang sangat membantu. Tentu saja pemahaman saya terbatas - sudah menjadi sifat manusia untuk dibatasi dalam semua pemahaman tidak peduli domain. Namun, maukah Anda mengatakan kepada saya pernyataan mana yang membawa Anda pada asumsi bahwa pemahaman saya tentang masalah khusus ini tidak lengkap?
Hubert Grzeskowiak
34

Singkat cerita:

Tidak ada Boolean di Bash

Bash memang memiliki ekspresi Boolean dalam hal perbandingan dan ketentuan. Yang mengatakan, apa yang dapat Anda nyatakan dan bandingkan di Bash adalah string dan angka. Itu dia.

Di mana pun Anda melihat trueatau falsedi Bash, itu adalah string atau perintah / builtin yang hanya digunakan untuk kode keluarnya.

Sintaks ini ...

if true; then ...

pada dasarnya ...

if COMMAND; then ...

Kondisi ini benar setiap kali perintah mengembalikan kode keluar 0. truedan falseBash bawaan dan kadang-kadang juga program mandiri yang tidak melakukan apa pun kecuali mengembalikan kode keluar yang sesuai.

Persyaratan di atas setara dengan:

COMMAND && ...

Saat menggunakan tanda kurung siku atau testperintah, Anda mengandalkan kode keluar dari konstruk itu. Perlu diingat bahwa [ ]dan [[ ]]juga hanya perintah / builtin seperti yang lain. Jadi ...

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

sesuai dengan

if COMMAND; then echo yes; fi

dan COMMANDini dia[[ 1 == 1 ]]

The if..then..fimembangun hanya sintaksis gula. Anda selalu dapat menjalankan perintah yang dipisahkan oleh amper ganda dan untuk efek yang sama:

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

Saat menggunakan truedan falsedalam konstruksi pengujian ini, Anda sebenarnya hanya meneruskan string "true"atau "false"ke perintah pengujian. Berikut ini sebuah contoh:

Percaya atau tidak tetapi semua kondisi tersebut menghasilkan hasil yang sama :

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

TL; DR; selalu membandingkan dengan string atau angka

Untuk memperjelas hal ini kepada pembaca di masa mendatang, saya akan merekomendasikan selalu menggunakan tanda kutip sekitar truedan false:

MELAKUKAN

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

JANGAN

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 `==`

Mungkin

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".
Hubert Grzeskowiak
sumber
Saya lebih suka menggunakan Tdan Fmenjelaskan bahwa itu bukan nilai boolean nyata.
phk
1
Saya tidak setuju dengan "selalu gunakan tanda kurung ganda di bash". Bahkan di hampir semua skrip yang saya tulis saya menggunakan kurung tunggal, kecuali ketika saya perlu melakukan pencocokan pola. Saya pikir orang harus memahami perbedaan antara [(yaitu test) dan [[dan menggunakan yang cocok untuk kebutuhannya.
Weijun Zhou
@ WeijunZhou keberatan menjelaskan kurung tunggal mana yang lebih baik?
Hubert Grzeskowiak
Ini lebih dari selera pribadi, saya hanya menemukan bahwa itu terlalu berani untuk mengatakan "Selalu gunakan tanda kurung ganda di bash". Tetapi ada beberapa kasus tepi yang saya gunakan. Kurung tunggal memungkinkan Anda untuk menentukan tes itu sendiri dalam var. Sebagai contoh yang disederhanakan, pertimbangkanif ....; then mytest='-gt'; else mytest='-eq'; fi; #several lines of code; if [ "$var1" "$mytest" "$var2" ]; then ...; fi
Weijun Zhou
@ WeijunZhou Contoh Anda adalah argumen kuat terhadap tanda kurung tunggal. Itu membuat kode jauh lebih sulit untuk dipahami dan membuka lebar jendela kesalahan. Kurung ganda lebih ketat dan mendorong kode pembersih.
Hubert Grzeskowiak
18

Dahulu, ketika semua yang kita miliki adalah sh, Boolean di mana ditangani dengan mengandalkan konvensi testprogram di mana testmengembalikan status keluar palsu jika dijalankan tanpa argumen.

Ini memungkinkan seseorang untuk memikirkan variabel yang tidak disetel sebagai false dan variabel disetel ke nilai apa pun sebagai benar. Hari ini, testadalah bawaan untuk Bash dan umumnya dikenal dengan alias satu-karakternya [(atau yang dapat dieksekusi untuk digunakan di shell yang tidak memilikinya, seperti yang dicatat 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

Karena mengutip konvensi, penulis skrip lebih suka menggunakan perintah majemuk [[yang meniru test, tetapi memiliki sintaksis yang lebih bagus: variabel dengan spasi tidak perlu dikutip; seseorang dapat menggunakan &&dan ||sebagai operator logis dengan prioritas yang aneh, dan tidak ada batasan POSIX pada jumlah istilah.

Misalnya, untuk menentukan apakah FLAG diatur dan COUNT angka lebih besar dari 1:

FLAG="u p"
COUNT=3

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

Hal-hal ini bisa membingungkan ketika spasi, string nol panjang, dan variabel nol semua diperlukan dan juga ketika skrip Anda perlu bekerja dengan beberapa shell.

Hbar
sumber
3
[bukan hanya alias di dalam bash. Alias ​​ini juga ada sebagai file biner (atau sebagai tautan yang menunjuk ke) dan dapat digunakan dengan telanjang sh. Periksa ls -l /usr/bin/\[. Dengan bash/ zshAnda sebaiknya menggunakan [[internal murni murni dan jauh lebih kuat.
dolmen
1
@dolmen [dan testjuga Bash SHELL BUILTIN COMMAND menurut halaman manual Bash, jadi seharusnya tidak ada masalah dalam kinerja. Hal yang sama dengan misalnya Dash. (/ bin / sh mungkin hanya symlink ke / bin / dash). Untuk menggunakan executable Anda harus menggunakan path lengkap yaitu /usr/bin/\[.
jarno
12

Bagaimana saya bisa mendeklarasikan dan menggunakan variabel Boolean dalam skrip shell?

Tidak seperti banyak bahasa pemrograman lain, Bash tidak memisahkan variabel-variabelnya dengan "ketik". [1]

Jadi jawabannya cukup jelas. Tidak ada variabel Boolean di Bash.

Namun:

Menggunakan pernyataan pernyataan, kita bisa membatasi penugasan nilai untuk variabel. [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"

The rpilihan di declaredan readonlydigunakan untuk negara secara eksplisit bahwa variabel yang dibaca . Saya harap tujuannya jelas.

sjsam
sumber
1
Kenapa kamu tidak lakukan saja declare -ir false=0 true=1? Apa keuntungan menggunakan array?
Benjamin W.
@BenjaminW. Saya hanya ingin menyebutkan tentang ropsi & readonlyperintah. Saya akan melakukannya dengan cara yang Anda sarankan dalam skrip saya
sjsam
mungkin saya melewatkan sesuatu tetapi mengapa tidak benar dan salah dinyatakan dengan cara ini menggunakan tanda dolar? $ true $ false
qodeninja
Secara harfiah hanya menyalin jawaban saya dan membuatnya lebih buruk.
Pertanyaan Quolonel
@QuolonelQuestions Variabel Bash tidak diketik , maka tidak ada gunanya mengatakan declare and use boolean variables. Kita bisa, dalam lebih dari satu cara, meniru / berasumsi bahwa suatu variabel memiliki tipe . Saya tidak melihat yang disebutkan di mana pun dalam jawaban Anda.
sjsam
10

Alih-alih memalsukan Boolean dan meninggalkan jebakan untuk pembaca masa depan, mengapa tidak menggunakan nilai yang lebih baik daripada benar dan salah?

Sebagai contoh:

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
Pirolistik
sumber
mengapa tidak bilangan bulat?
phil294
3
@ Blauhirn karena integer digunakan secara berbeda tergantung pada bahasa. Dalam beberapa bahasa 0memaksa untuk falsedan 1untuk true. Sehubungan dengan kode keluar program (yang digunakan bash secara historis) itu 0untuk hasil positif atau truedan semua yang lain adalah negatif / kesalahan atau false.
Hubert Grzeskowiak
7

POSIX (Antarmuka Sistem Operasi Portabel)

Saya rindu di sini titik kunci, yaitu portabilitas. Itu sebabnya tajuk saya memiliki POSIX sendiri.

Pada dasarnya, semua jawaban yang dipilih sudah benar, dengan pengecualian mereka terlalu banyak Bash- spesifik.

Pada dasarnya, saya hanya ingin menambahkan lebih banyak informasi tentang portabilitas.


  1. [dan ]tanda kurung seperti dalam [ "$var" = true ]tidak diperlukan, dan Anda dapat menghilangkannya dan menggunakan testperintah secara langsung:

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

    Catatan penting: Saya tidak lagi merekomendasikan ini karena sudah mulai ditinggalkan dan lebih sulit untuk menggabungkan beberapa pernyataan.

  2. Bayangkan apa kata-kata itu truedan falseartinya bagi cangkang itu, ujilah sendiri:

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

    Tetapi menggunakan kutipan:

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

    Hal yang sama berlaku untuk:

    echo $(( "false" ))

    Shell tidak dapat mengartikannya selain string. Saya harap Anda mendapatkan ide seberapa baik menggunakan kata kunci yang tepat tanpa tanda kutip .

    Tapi tidak ada yang mengatakannya di jawaban sebelumnya.

  3. Apa artinya ini? Nah, beberapa hal.

    • Anda harus membiasakan diri dengan kata kunci Boolean yang sebenarnya diperlakukan seperti angka, yaitu true= 0dan false= 1, ingat semua nilai bukan nol diperlakukan seperti false.

    • Karena mereka diperlakukan sebagai angka, Anda harus memperlakukannya seperti itu juga, yaitu jika Anda mendefinisikan variabel katakan:

      var_bool=true
      echo "$var_bool"
       true

      Anda dapat membuat nilai sebaliknya dengan:

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

    Seperti yang Anda lihat sendiri, shell mencetak truestring untuk pertama kali Anda menggunakannya, tetapi sejak itu, semuanya bekerja melalui angka yang 0mewakili trueatau 1mewakili false.


Akhirnya, apa yang harus Anda lakukan dengan semua informasi itu

  • Pertama, satu kebiasaan baik adalah menugaskan 0alih-alih true; 1bukannya false.

  • Kebiasaan baik kedua adalah menguji apakah variabelnya sama dengan nol:

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

Mengenai sintaks, ini adalah metodologi sederhana yang saya gunakan (dengan contoh) untuk secara konsisten dan waras mengelola logika Boolean:

# 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

Jika variabel tidak pernah dideklarasikan, jawabannya adalah: # false

Jadi, cara sederhana untuk mengatur variabel ke true (menggunakan metodologi sintaks ini) adalah var=1,; sebaliknya var='',.

Referensi:

-n = Benar jika panjang string var bukan nol.

-z = Benar jika panjang string var adalah nol.

Eric P.
sumber
5

Dalam banyak bahasa pemrograman, tipe Boolean adalah, atau diimplementasikan sebagai, subtipe integer, di mana trueberperilaku seperti 1dan falseberperilaku seperti 0:

Secara matematis , aljabar Boolean menyerupai bilangan bulat modul hitung 2. Karena itu, jika bahasa tidak menyediakan tipe Boolean asli, solusi yang paling alami dan efisien adalah dengan menggunakan bilangan bulat. Ini berfungsi dengan hampir semua bahasa. Misalnya, di Bash Anda dapat melakukan:

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

man bash :

((ekspresi))

Ekspresi dievaluasi sesuai dengan aturan yang dijelaskan di bawah ini di bawah EVALUASI ARITHMETIC. Jika nilai ekspresi adalah bukan nol, status pengembalian adalah 0; jika tidak, status pengembaliannya adalah 1. Ini persis sama dengan membiarkan "ekspresi".

Cyker
sumber
5

Bill Parker ditolak , karena definisinya dibalik dari konvensi kode normal. Biasanya, true didefinisikan sebagai 0 dan false didefinisikan sebagai bukan nol. 1 akan bekerja untuk false, seperti juga 9999 dan -1. Sama dengan nilai-nilai fungsi kembali - 0 adalah sukses dan apa pun bukan nol kegagalan. Maaf, saya belum memiliki kredibilitas jalan untuk memilih atau membalasnya secara langsung.

Bash merekomendasikan penggunaan kurung ganda sekarang sebagai kebiasaan, bukan kurung tunggal, dan tautan yang diberikan Mike Holt menjelaskan perbedaan cara kerjanya. 7.3. Operator Perbandingan Lainnya

Untuk satu hal, -eqadalah operator numerik, sehingga memiliki kodenya

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

akan mengeluarkan pernyataan kesalahan, mengharapkan ekspresi integer. Ini berlaku untuk salah satu parameter, karena tidak ada nilai integer. Namun, jika kita menempatkan tanda kurung ganda di sekitarnya, itu tidak akan mengeluarkan pernyataan kesalahan, tetapi akan menghasilkan nilai yang salah (baik, dalam 50% dari permutasi yang mungkin). Ini akan mengevaluasi ke [[0 -eq true]] = sukses, tetapi juga ke [[0 -eq false]] = sukses, yang salah (hmmm .... bagaimana dengan buildin yang menjadi nilai numerik?).

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

Ada permutasi lain dari kondisi yang akan memberikan output yang salah juga. Pada dasarnya, apa pun (selain kondisi kesalahan yang tercantum di atas) yang menetapkan variabel ke nilai numerik dan membandingkannya dengan true / false builtin, atau menetapkan variabel ke true / false builtin dan membandingkannya dengan nilai numerik. Juga, apa pun yang menetapkan variabel ke true / false builtin dan melakukan perbandingan menggunakan -eq. Jadi hindari -equntuk perbandingan Boolean dan hindari menggunakan nilai numerik untuk perbandingan Boolean. Berikut ringkasan permutasi yang akan memberikan hasil yang tidak valid:

# 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

Jadi, sekarang untuk apa yang berhasil. Gunakan benar / salah bawaan untuk perbandingan Anda dan evaluasi Anda (seperti dicatat Mike Hunt, jangan menyertakannya dalam tanda kutip). Kemudian gunakan tanda sama dengan tunggal atau ganda atau ganda (= atau ==) dan kurung tunggal atau ganda ([] atau [[]]). Secara pribadi, saya suka tanda sama dengan ganda, karena mengingatkan saya pada perbandingan logis dalam bahasa pemrograman lain, dan tanda kutip ganda hanya karena saya suka mengetik. Jadi ini bekerja:

# 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

Itu dia.

Randyman99
sumber
2
The true/ falsebuilt-in yang tidak digunakan di sini (mengabaikan apa sintaks dari beberapa editor mungkin menyiratkan), terutama di […]kasus Anda dapat menganggapnya sebagai string sederhana di sini (yang diberikan sebagai parameter untuk [perintah).
phk
Anda memilikinya sekarang.
Peter Mortensen
4

Temuan dan saran saya sedikit berbeda dari pos lainnya. Saya menemukan bahwa saya dapat menggunakan "boolean" pada dasarnya seperti yang dilakukan orang dalam bahasa "biasa", tanpa "hoop jumping" yang disarankan ...

Tidak perlu []atau perbandingan string eksplisit ... Saya mencoba beberapa distribusi Linux. Saya menguji Bash, Dash, dan BusyBox . Hasilnya selalu sama. Saya tidak yakin apa yang dibicarakan oleh posting terpilih teratas asli. Mungkin waktu telah berubah dan hanya itu yang ada di sana?

Jika Anda menetapkan variabel ke true, itu kemudian mengevaluasi sebagai "setuju" dalam kondisi. Setel ke false, dan ini dievaluasi menjadi "negatif". Sangat mudah! Satu-satunya peringatan, adalah bahwa variabel yang tidak terdefinisi juga mengevaluasi seperti benar ! Akan lebih baik jika melakukan yang sebaliknya (seperti dalam kebanyakan bahasa), tetapi itulah triknya - Anda hanya perlu secara eksplisit menginisialisasi boolean Anda menjadi benar atau salah .

Mengapa bisa seperti ini? Jawaban itu dua kali lipat. A) benar / salah dalam shell benar-benar berarti "tidak ada kesalahan" vs "kesalahan" (yaitu 0 vs yang lain). B) benar / salah bukan nilai - melainkan pernyataan dalam skrip shell! Mengenai poin kedua, mengeksekusi trueatau falseon line dengan sendirinya menetapkan nilai kembali untuk blok Anda ke nilai itu, yaitu falsedeklarasi "kesalahan yang ditemui", di mana benar "membersihkan" itu. Menggunakannya dengan penugasan ke variabel "mengembalikan" itu ke dalam variabel. Sebuah terdefinisi variabel mengevaluasi seperti truedalam bersyarat karena sama-sama mewakili 0 atau "tidak kesalahan yang ditemui".

Lihat contoh garis Bash dan hasil di bawah ini. Uji sendiri jika Anda ingin mengonfirmasi ...

#!/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;

Hasil

when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
Terima kasih
sumber
1

Ini adalah contoh sederhana yang cocok untuk saya:

temp1=true
temp2=false

if [ "$temp1" = true ] || [ "$temp2" = true ]
then
    echo "Do something." 
else
    echo "Do something else."
fi
Harsimranjit Singh Kler
sumber
1

Berikut ini adalah implementasi dari tangan pendek 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
}

Contoh 1

my_boolean=true

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

Contoh 2

my_boolean=false
! _if ${my_boolean} && echo "Not True is True"
llundin
sumber
Ya, dekomposisi fungsional kurang dihargai.
Peter Mortensen
1

Saya menemukan jawaban yang ada membingungkan.

Secara pribadi, saya hanya ingin memiliki sesuatu yang terlihat dan berfungsi seperti C.

Cuplikan ini berfungsi beberapa kali sehari dalam produksi:

snapshotEvents=true

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

dan untuk membuat semua orang senang, saya menguji:

snapshotEvents=false

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

Yang juga berfungsi dengan baik.

The $snapshotEventsmengevaluasi isi dari nilai variabel. Jadi kamu butuh $.

Anda tidak benar-benar membutuhkan tanda kurung, saya hanya menemukan itu membantu.

akan
sumber
2
Di mana Anda menghapus tanda kurung, inilah jawaban asli @ miku di atas.
dolmen
1
Tanpa tanda kurung ekspresi tidak akan dievaluasi.
akan
@ akan ya itu. Anda tidak memerlukan () s.
phil294
1
@ Blauhirn ... Hai, saya mendasarkan komentar saya pada percobaan dengan GNU Bash pada Linux Mint / Ubuntu PC. Anda mungkin benar dalam teori () -s tidak diperlukan. Satu-satunya tanggapan saya, adalah mencobanya, tampaknya tergantung pada versi Bash, ekspresi atau konteks aktual dan semacamnya.
akan
1

Berikut ini adalah peningkatan pada jawaban asli miku yang membahas kekhawatiran Dennis Williamson tentang kasus di mana variabel tidak disetel:

the_world_is_flat=true

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

Dan untuk menguji apakah variabelnya adalah false:

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

Tentang kasus lain dengan konten jahat dalam variabel, ini merupakan masalah dengan input eksternal apa pun yang dimasukkan ke program.

Setiap input eksternal harus divalidasi sebelum memercayainya. Tetapi validasi itu harus dilakukan sekali saja, ketika input itu diterima.

Tidak harus memengaruhi kinerja program dengan melakukannya pada setiap penggunaan variabel seperti yang disarankan Dennis Williamson .

dolmen
sumber
1

Anda dapat menggunakan shFlags .

Ini memberi Anda opsi untuk mendefinisikan: DEFINE_bool

Contoh:

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

Dari baris perintah Anda dapat mendefinisikan:

sh script.sh --bigmenu
sh script.sh --nobigmenu # False
gogasca
sumber
GFlags tidak masuk akal dalam jawaban ini - ini adalah pustaka C ++. Itu tidak bisa langsung digunakan dalam skrip shell.
Jonathan Cross
Respons yang diperbarui terhadap shFlags yang merupakan port dari GFlags ke shell.
gogasca
0

Ini adalah tes kecepatan tentang berbagai cara untuk menguji nilai "Boolean" di 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

Itu akan mencetak sesuatu seperti

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
jarno
sumber
-2

Alternatif - gunakan fungsi

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

Mendefinisikan fungsi kurang intuitif, tetapi memeriksa nilai kembalinya sangat mudah.

Johnraff
sumber
1
Ini bukan variabel yang bisa Anda uji, tetapi fungsi konstan
jarno
@jarno Apakah menguji nilai balik suatu fungsi berbeda dari menguji suatu variabel, untuk keperluan skrip?
johnraff
Nah, pertanyaannya adalah tentang variabel.
jarno
Benar, meskipun penggunaan dalam skrip shell akan sama.
johnraff
-2

Bash benar-benar membingungkan masalah dengan orang-orang seperti [, [[, ((, $((, dll

Semua menginjak ruang kode masing-masing. Saya kira ini sebagian besar bersejarah, di mana Bash harus berpura-pura menjadi shsesekali.

Sebagian besar waktu, saya hanya bisa memilih metode dan tetap menggunakannya. Dalam hal ini, saya cenderung mendeklarasikan (lebih disukai dalam file perpustakaan umum yang dapat saya sertakan dengan .skrip aktual saya).

TRUE=1; FALSE=0

Saya kemudian dapat menggunakan ((... ))operator aritmatika untuk menguji demikian.

testvar=$FALSE

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

if (( testvar == TRUE )); then
    # Do stuff because the directory does exist
fi
  1. Anda harus disiplin. Anda testvarharus diatur ke $TRUEatau $FALSEsetiap saat.

  2. Di ((... ))pembanding, Anda tidak perlu yang sebelumnya $, yang membuatnya lebih mudah dibaca.

  3. Saya dapat menggunakan ((... ))karena $TRUE=1dan $FALSE=0, yaitu nilai numerik.

  4. Kelemahannya adalah harus menggunakan $sesekali:

    testvar=$TRUE

    yang tidak begitu cantik.

Ini bukan solusi yang sempurna, tetapi mencakup setiap kasus yang saya butuhkan dari tes tersebut.

Bill Parker
sumber
2
Anda harus mendeklarasikan konstanta Anda hanya baca. Harap selalu gunakan kurung keriting saat menggunakan variabel. Ini adalah konvensi yang setiap orang harus berpegang pada IMHO. Kelemahan besar dari solusi ini adalah Anda tidak dapat mencampur ekspresi aljabar dengan flag uji atau perbandingan string.
Hubert Grzeskowiak