Apakah ada perbedaan besar ketika membandingkan suatu variabel sebagai string atau sebagai int

22

Karena penasaran, ketika melakukan perbandingan variabel bash (nilainya menjadi an integer) dimungkinkan untuk mengujinya terhadap beberapa nilai yang telah ditentukan baik dinyatakan sebagai intatau sebagai a string.

Contoh skrip :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Keluaran :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

dan

$  ./param.sh blah
$

Kedua fungsi berperilaku dengan cara yang sama, jadi saya ingin tahu apakah ada cara yang lebih disukai ketika memeriksa variabel integer? Saya akan pergi untuk memeriksa intversus intkarena lebih ketat tapi saya ingin tahu apakah ada penarikan kembali melakukannya dengan string?

Dalam hal ini, f2()juga lebih ketat tentang perbandingan, yaitu melewati nilai desimal akan mematahkannya, sedangkan f1()akan membawanya tidak ada masalah.

fduff
sumber
Perhatikan bahwa Bash tidak benar-benar memiliki tipe data Integer. Anda pada dasarnya hanya bisa mengisyaratkan Bash untuk memperlakukan String sebagai integer.
helpermethodhod

Jawaban:

18

Yap, banyak sekali perbedaannya. Misalnya, =memeriksa persamaan string yang tepat, tetapi -eqmengevaluasi kedua ekspresi secara hitung sebelum memeriksa kesetaraan:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Selain itu, string kosong secara numerik sama dengan nol:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

Dan seluruh kelas perbedaan lainnya muncul ketika Anda membawa operator pembanding - mempertimbangkan <vs -lt, misalnya:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Ini karena string "2" secara alfabetis setelah string "10" (karena 1 muncul sebelum 2), tetapi angka "2" secara numerik kurang dari angka "10".

godlygeek
sumber
2
Jangan lupa ada juga (( ... ))untuk operasi numerik. (( " 1 " == 1 )) && echo yes || echo nohasil dalamyes
Patrick
7

Perbandingan bilangan bulat vs bilangan bulat menjadi paling signifikan ketika Anda membandingkan lebih besar atau lebih kecil dari:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

Yang pertama gagal karena 9 muncul setelah 11 ketika diurutkan secara leksikografis.

Perhatikan bahwa menggunakan tanda kutip tidak menentukan apakah Anda membandingkan string atau angka, yang dilakukan operator. Anda dapat menambah atau menghapus tanda kutip di atas, tidak ada bedanya. Bash menangkap variabel yang tidak ditentukan dalam kurung ganda, jadi tanda kutip tidak diperlukan. Menggunakan kutipan dengan tanda kurung tunggal untuk pengujian numerik tidak akan menyelamatkan Anda sejak:

[ "" -lt 11 ]

tetap merupakan kesalahan ("diperlukan ekspresi integer"). Kutipan adalah perlindungan yang efektif dengan perbandingan string dalam kurung tunggal:

[ "" \< 11 ]

Catatan dalam kurung ganda , ""akan -eq 0tetapi tidak == 0.

goldilocks
sumber
1
Dalam bash, tidak perlu mengutip variabel dalam tanda kurung ganda: builtin [[cukup pintar untuk mengingat di mana variabel berada, dan itu tidak akan tertipu oleh variabel kosong. Kurung tunggal ( [) tidak memiliki fitur ini, dan memerlukan penawaran.
glenn jackman
@glennjackman Tidak memperhatikan itu. [[ -lt 11 ]]adalah kesalahan, tetapi nothing=; [[ $nothing -lt 11 ]]tidak. Saya telah mengerjakan ulang paragraf terakhir sedikit.
goldilocks
2

Selain apa yang telah dikatakan.
Membandingkan kesetaraan lebih cepat dengan angka, meskipun dalam skrip shell jarang Anda memerlukan perhitungan cepat.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s
Emmanuel
sumber
Mengingat mereka melakukan hal-hal yang berbeda, saya akan mengatakan kinerjanya tidak relevan - Anda perlu menggunakan yang melakukan apa yang Anda inginkan.
godlygeek
@ godlygeek Perbandingan kesetaraan dari suatu variabel dapat dicapai dengan dua arah. "-eq" lebih cepat.
Emmanuel
Mereka menguji definisi kesetaraan yang berbeda. Jika Anda ingin menjawab pertanyaan "Apakah variabel ini memiliki string yang tepat 123", Anda hanya dapat menggunakan =, karena menggunakan -eqjuga cocok dengan "+123". Jika Anda ingin tahu "Apakah variabel ini, ketika dievaluasi sebagai ekspresi aritmatika, bandingkan dengan 123", Anda hanya bisa menggunakan -eq. Satu-satunya waktu saya bisa melihat di mana seorang programmer tidak akan peduli definisi kesetaraan mana yang digunakan adalah ketika dia tahu bahwa isi variabel dibatasi pada pola tertentu sebelumnya.
godlygeek
@ godlygeek menarik, pertanyaannya adalah tentang membandingkan kesetaraan angka seolah-olah mereka adalah string, apakah itu cocok dengan kasus variabel dibatasi sebelumnya untuk pola tertentu?
Emmanuel
Contoh Anda ( b=234) cocok dengan pola itu - Anda tahu itu bukan +234 atau "234" atau "233 +1", karena Anda menetapkannya sendiri, jadi Anda tahu bahwa itu membandingkannya sebagai string dan sebagai angka sama-sama valid. Tetapi skrip OP, karena dibutuhkan input sebagai argumen baris perintah, tidak memiliki kendala itu - pertimbangkan menyebutnya sebagai ./param.sh 0+1atau./param.sh " 1"
godlygeek