Operator Kesetaraan Bash (==, -eq)

136

Bisakah seseorang tolong jelaskan perbedaan antara -eqdan ==dalam skrip bash?

Apakah ada perbedaan antara yang berikut ini?

[ $a -eq $b ] dan [ $a == $b ]

Apakah hanya itu yang ==hanya digunakan ketika variabel berisi angka?

Victor Brunell
sumber

Jawaban:

187

Ini sebaliknya: =dan ==untuk perbandingan string, -eqadalah untuk numerik. -eqadalah dalam keluarga yang sama seperti -lt, -le, -gt, -ge, dan -ne, jika yang membantu Anda ingat yang mana.

==omong-omong, omong-omong. Lebih baik menggunakan POSIX =. Dalam bash keduanya sama, dan di sh polos =adalah satu-satunya yang dijamin untuk bekerja.

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(Catatan: Kutip ekspansi variabel itu! Jangan tinggalkan tanda kutip ganda di atas.)

Jika Anda menulis #!/bin/bashskrip maka saya sarankan menggunakan [[sebagai gantinya . Bentuk ganda memiliki lebih banyak fitur, sintaksis yang lebih alami, dan lebih sedikit Gotcha yang akan membuat Anda tersandung. Kutipan ganda tidak lagi diperlukan $a, misalnya:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

Lihat juga:

John Kugelman
sumber
1
@DJCrashdummy, [[secara keseluruhan adalah ksh-isme era 1980-an yang diadopsi oleh bash (dan banyak kerang lainnya). Itulah intinya - jika Anda memiliki [[ semuanya , maka Anda dapat dengan aman mengasumsikan bahwa semua ekstensi ksh diimplementasikan di sekitarnya ( fnmatch()pencocokan pola-gaya, ERE ekspresi reguler dengan =~, dan ya, penindasan string-splitting dan penggumpalan sistem file) akan menjadi tersedia. Karena [[sintaksinya adalah non-POSIX secara keseluruhan, tidak ada kerugian portabilitas tambahan dengan mengasumsikan bahwa fitur yang dilahirkannya akan tersedia.
Charles Duffy
1
@DJCrashdummy, ... link Anda menunjukkan poin dengan benar bahwa tanda kutip kadang-kadang diperlukan di sisi kanan dari [[ $var = $pattern ]], jika Anda ingin apa yang seharusnya dapat diartikan sebagai pola fnmatch untuk bukan diartikan sebagai string literal. String footidak memiliki interpretasi non-literal, membuat meninggalkan kutipan sepenuhnya aman; itu hanya jika OP ingin mencocokkan, katakanlah, foo*(dengan tanda bintang sebagai literal, tidak berarti apa pun yang dapat muncul setelah string foo) yang mengutip atau melarikan diri akan diperlukan.
Charles Duffy
@CharlesDuffy sayangnya saya tidak tahu apa yang Anda maksudkan, karena komentar saya tampaknya dihapus dan saya tidak ingat karena sudah cukup lama. :-(
DJCrashdummy
@DJCrashdummy, ... ya, saya berasumsi Anda akan mencabutnya sendiri. Pada dasarnya, itu adalah keberatan tentang ekspansi [[tanda kutip di dalam menjadi bashism (menunjukkan bahwa itu adalah satu-satunya alasan Anda tidak memberikan jawaban +1).
Charles Duffy
@CharlesDuffy tidak, itu bukan satu-satunya alasan: selain fakta bahwa saya tidak suka bash-isme, ksh-isme dll untuk tugas-tugas sederhana (itu hanya menyebabkan masalah dan membingungkan pemula), saya tidak mengerti mengapa mencampur satu kurung kurawal [ ... ]dan dobel tanda sama dengan ==. : - /
DJCrashdummy
27

Itu tergantung pada Uji Konstruksi di sekitar operator. Pilihan Anda adalah tanda kurung ganda, kawat gigi ganda, kawat gigi tunggal, atau tes

Jika Anda menggunakan ((...)) , Anda menguji ekuitas aritmatika dengan ==seperti pada C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(Catatan: 0berarti truedalam arti Unix dan bukan nol adalah tes gagal)

Menggunakan -eqbagian dalam tanda kurung ganda adalah kesalahan sintaksis.

Jika Anda menggunakan [...] (atau penjepit tunggal) atau [[...]] (atau penjepit ganda), atau testAnda dapat menggunakan salah satu dari -eq, -ne, -lt, -le, -gt, atau -ge sebagai perbandingan aritmatika .

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

Bagian ==dalam kawat gigi tunggal atau ganda (atau testperintah) adalah salah satu operator perbandingan string :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

Sebagai operator string, =sama dengan ==dan perhatikan spasi putih di sekitar =atau ==yang diperlukan.

Meskipun Anda dapat melakukan [[ 1 == 1 ]]atau [[ $(( 1+1 )) == 2 ]]sedang menguji kesetaraan string - bukan kesetaraan aritmatika.

Jadi -eqmenghasilkan hasil yang mungkin diharapkan bahwa nilai integer 1+1sama dengan 2meskipun RH adalah string dan memiliki ruang tambahan:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

Sementara perbandingan string yang sama mengambil ruang tambahan dan karenanya perbandingan string gagal:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

Dan perbandingan string yang salah dapat menghasilkan jawaban yang salah sepenuhnya. '10' secara leksikografis kurang dari '2', sehingga perbandingan string mengembalikan trueatau 0. Begitu banyak yang digigit oleh bug ini:

$ [[ 10 < 2 ]]; echo $?
0

vs tes yang benar untuk 10 secara aritmetika kurang dari 2:

$ [[ 10 -lt 2 ]]; echo $?
1

Dalam komentar, ada pertanyaan tentang alasan teknis menggunakan integer -eqpada string mengembalikan True untuk string yang tidak sama:

$ [[ "yes" -eq "no" ]]; echo $?
0

Alasannya adalah bahwa Bash tidak diketik . The -eqmenyebabkan string ditafsirkan sebagai bilangan bulat jika mungkin termasuk konversi dasar:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

Dan 0jika Bash berpikir itu hanya string:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

Jadi [[ "yes" -eq "no" ]]setara dengan[[ 0 -eq 0 ]]

Catatan terakhir: Banyak ekstensi spesifik Bash untuk Konstruk Uji tidak POSIX dan karenanya akan gagal di shell lain. Kerang lain umumnya tidak mendukung [[...]]dan ((...))atau== .

dawg
sumber
Saya ingin tahu tentang alasan teknis untuk [[ "yes" -eq "no" ]]mengembalikan True. Bagaimana bash memaksa string ini ke nilai integer yang dapat dibandingkan? ;-)
odony
4
Variabel Bash tidak diketik sehingga [[ "yes" -eq "no" ]]sama dengan [[ "yes" -eq 0 ]] atau [[ "yes" -eq "any_noninteger_string" ]]- Semua Benar. The -eqpasukan bilangan bulat perbandingan. Ini "yes"ditafsirkan sebagai bilangan bulat 0; perbandingannya Benar jika bilangan bulat lainnya baik 0atau hasil string 0.
dawg
Boo, desisnya kembali: menunjukkan (tidak dapat dipindahkan) ==dalam sampel kode dan hanya menyebutkan (portabel, standar) di =bawahnya.
Charles Duffy
24

==adalah alias spesifik-bash untuk =dan melakukan perbandingan string (leksikal) alih-alih perbandingan numerik. eqmenjadi perbandingan angka tentu saja.

Akhirnya, saya biasanya lebih suka menggunakan formulir if [ "$a" == "$b" ]

Elliott Frisch
sumber
15
Menggunakan di ==sini adalah bentuk yang buruk, karena hanya =ditentukan oleh POSIX.
Charles Duffy
9
Jika Anda benar-benar bersikeras untuk menggunakannya ==maka letakkan di antara [[dan ]]. (Dan pastikan bahwa baris pertama skrip Anda ditentukan untuk digunakan /bin/bash.)
holgero
18

Guys: Beberapa jawaban menunjukkan contoh berbahaya. Contoh OP [ $a == $b ]secara khusus menggunakan substitusi variabel yang tidak dikutip (per Oktober '17 sunting). Untuk [...]itu aman untuk kesetaraan string.

Tetapi jika Anda akan menghitung alternatif seperti [[...]], Anda harus memberi tahu juga bahwa sisi kanan harus dikutip. Jika tidak dikutip, itu adalah pencocokan pola! (Dari halaman bash man: "Bagian mana pun dari pola dapat dikutip untuk memaksa agar dicocokkan sebagai string.").

Di sini, di bash, dua pernyataan yang menghasilkan "ya" adalah pencocokan pola, tiga lainnya adalah persamaan string:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$
bk-se
sumber
2
Perlu [ "$lht" = "$rht" ] dengan kutipan agar dapat diandalkan bahkan untuk kesetaraan. Jika Anda memiliki file yang dibuat touch 'Afoo -o AB', [ $lft = $rht ]akan mengembalikan true, meskipun nama file itu sama sekali tidak identik AB.
Charles Duffy