Bash jika [false]; mengembalikan true

151

Sudah belajar bash minggu ini dan mengalami kesulitan.

#!/bin/sh

if [ false ]; then
    echo "True"
else
    echo "False"
fi

Ini akan selalu menghasilkan True walaupun kondisi tampaknya menunjukkan sebaliknya. Jika saya melepas tanda kurung []maka itu berfungsi, tapi saya tidak mengerti mengapa.

sepuluh mil
sumber
3
Ini ada sesuatu untuk Anda mulai.
keyser
10
BTW, skrip yang dimulai dengan #!/bin/shbukan skrip bash - skrip POSIX sh. Bahkan jika penerjemah sh POSIX pada sistem Anda disediakan oleh bash, ia mematikan banyak ekstensi. Jika Anda ingin menulis skrip bash, gunakan #!/bin/bashatau padanannya yang sesuai secara lokal ( #!/usr/bin/env bashuntuk menggunakan penerjemah bash pertama dalam PATH).
Charles Duffy
Ini [[ false ]]juga mempengaruhi .
CMCDragonkai

Jawaban:

192

Anda menjalankan perintah [(alias test) dengan argumen "false", bukan menjalankan perintah false. Karena "false" adalah string yang tidak kosong, testperintah selalu berhasil. Untuk benar-benar menjalankan perintah, lepaskan [perintah.

if false; then
   echo "True"
else
   echo "False"
fi
chepner
sumber
4
Gagasan salah menjadi perintah, atau bahkan string, tampaknya aneh bagi saya, tapi saya kira itu berhasil. Terima kasih.
Tenmiles
31
bashtidak memiliki tipe data Boolean, jadi tidak ada kata kunci yang mewakili benar dan salah. The ifpernyataan hanya memeriksa apakah perintah yang Anda berikan berhasil atau gagal. The testperintah mengambil ekspresi dan berhasil jika ungkapan benar; string yang tidak kosong adalah ekspresi yang dievaluasi sebagai true, sama seperti pada kebanyakan bahasa pemrograman lainnya. falseadalah perintah yang selalu gagal. (Dengan analogi, trueadalah perintah yang selalu berhasil.)
chepner
2
if [[ false ]];mengembalikan true juga. Seperti halnya if [[ 0 ]];. Dan if [[ /usr/bin/false ]];tidak melewatkan blok juga. Bagaimana false atau 0 bisa bernilai nol? Sialan ini membuat frustrasi. Jika itu penting, OS X 10.8; Bash 3.
jww
7
Jangan salah falseuntuk konstanta Boolean atau 0untuk literal integer; keduanya hanyalah string biasa. [[ x ]]sama dengan [[ -n x ]]untuk string apa pun xyang tidak dimulai dengan tanda hubung. bashtidak memiliki setiap konstanta Boolean dalam konteks apapun. String yang terlihat seperti bilangan bulat diperlakukan seperti di dalam (( ... ))atau dengan operator seperti -eqatau -ltdi dalam [[ ... ]].
chepner
2
@ tbc0, ada lebih banyak masalah yang dihadapi daripada sekadar bagaimana sesuatu membaca. Jika variabel diatur oleh kode tidak sepenuhnya di bawah kendali Anda (atau yang dapat dimanipulasi secara eksternal, baik itu dengan nama file yang tidak biasa atau sebaliknya), if $predicatedapat dengan mudah disalahgunakan untuk mengeksekusi kode bermusuhan dengan cara yang if [[ $predicate ]]tidak bisa.
Charles Duffy
55

Primer Boolean Cepat untuk Bash

The ifPernyataan mengambil perintah sebagai argumen (seperti halnya &&, ||, dll). Kode hasil integer dari perintah ditafsirkan sebagai boolean (0 / null = true, 1 / else = false).

The testPernyataan mengambil operator dan operan sebagai argumen dan mengembalikan kode hasil dalam format yang sama seperti if. Sebuah alias dari testpernyataan tersebut adalah [, yang sering digunakan dengan ifuntuk melakukan perbandingan yang lebih kompleks.

The truedan falsepernyataan melakukan apa-apa dan kode hasil (0 dan 1, masing-masing). Sehingga mereka dapat digunakan sebagai boolean literal di Bash. Tetapi jika Anda meletakkan pernyataan di tempat yang ditafsirkan sebagai string, Anda akan mengalami masalah. Dalam kasus Anda:

if [ foo ]; then ... # "if the string 'foo' is non-empty, return true"
if foo; then ...     # "if the command foo succeeds, return true"

Begitu:

if [ true  ] ; then echo "This text will always appear." ; fi;
if [ false ] ; then echo "This text will always appear." ; fi;
if true      ; then echo "This text will always appear." ; fi;
if false     ; then echo "This text will never appear."  ; fi;

Hal ini mirip dengan melakukan sesuatu seperti echo '$foo'vs echo "$foo".

Saat menggunakan testpernyataan, hasilnya tergantung pada operator yang digunakan.

if [ "$foo" = "$bar" ]   # true if the string values of $foo and $bar are equal
if [ "$foo" -eq "$bar" ] # true if the integer values of $foo and $bar are equal
if [ -f "$foo" ]         # true if $foo is a file that exists (by path)
if [ "$foo" ]            # true if $foo evaluates to a non-empty string
if foo                   # true if foo, as a command/subroutine,
                         # evaluates to true/success (returns 0 or null)

Singkatnya , jika Anda hanya ingin menguji sesuatu sebagai lulus / gagal (alias "benar" / "salah"), maka berikan perintah ke pernyataan Anda ifatau lainnya &&, tanpa tanda kurung. Untuk perbandingan yang rumit, gunakan tanda kurung dengan operator yang tepat.

Dan ya, saya sadar tidak ada yang namanya tipe boolean asli di Bash, dan itu ifdan [dan truesecara teknis "perintah" dan bukan "pernyataan"; ini hanya penjelasan yang sangat mendasar dan fungsional.

Beejor
sumber
1
FYI, jika Anda ingin menggunakan 1/0 sebagai Benar / Salah dalam kode Anda, ini if (( $x )); then echo "Hello"; fimenunjukkan pesan untuk x=1tetapi tidak untuk x=0atau x=(tidak ditentukan).
Jonathan H
3

Saya menemukan bahwa saya dapat melakukan beberapa logika dasar dengan menjalankan sesuatu seperti:

A=true
B=true
if ($A && $B); then
    C=true
else
    C=false
fi
echo $C
Rodrigo
sumber
6
Seseorang bisa , tapi itu ide yang sangat buruk. ( $A && $B )adalah operasi yang sangat tidak efisien - Anda menelurkan subproses melalui pemanggilan fork(), kemudian memisahkan string dari konten $Auntuk menghasilkan daftar elemen (dalam hal ini ukuran satu), dan mengevaluasi setiap elemen sebagai bola (dalam hal ini satu yang mengevaluasi sendiri), kemudian menjalankan hasilnya sebagai perintah (dalam hal ini, perintah builtin).
Charles Duffy
3
Selain itu, jika string Anda truedan falseberasal dari tempat lain, ada risiko bahwa mereka sebenarnya bisa mengandung konten selain trueatau false, dan Anda bisa mendapatkan perilaku tak terduga hingga dan termasuk eksekusi perintah sewenang-wenang.
Charles Duffy
6
Jauh lebih aman untuk menulis A=1; B=1dan if (( A && B ))- memperlakukannya sebagai angka - atau [[ $A && $B ]], yang memperlakukan string kosong sebagai false dan string non-kosong sebagai true.
Charles Duffy
3
(perhatikan bahwa saya hanya menggunakan nama variabel all-caps di atas untuk mengikuti konvensi yang dibuat oleh jawaban - POSIX sebenarnya secara eksplisit menentukan semua nama huruf besar yang akan digunakan untuk variabel lingkungan dan shell builtin, dan cadangan nama huruf kecil untuk penggunaan aplikasi; untuk menghindari konflik dengan lingkungan OS di masa depan, khususnya mengingat bahwa menetapkan variabel shell menimpa variabel lingkungan seperti apa pun, selalu ideal untuk menghormati konvensi itu dalam skrip sendiri).
Charles Duffy
Terima kasih atas wawasannya!
Rodrigo
2

Menggunakan true / false menghapus beberapa ...

#! /bin/bash    
#  true_or_false.bash

[ "$(basename $0)" == "bash" ] && sourced=true || sourced=false

$sourced && echo "SOURCED"
$sourced || echo "CALLED"

# Just an alternate way:
! $sourced  &&  echo "CALLED " ||  echo "SOURCED"

$sourced && return || exit
psh
sumber
Saya menemukan ini membantu tetapi di ubuntu 18,04 $ 0 adalah "-bash" dan perintah basename melemparkan kesalahan. Mengubah tes itu untuk [[ "$0" =~ "bash" ]] membuat skrip bekerja untuk saya.
WiringHarness