Tidak ada alasan mengapa
[[ $a = a|b ]]
Harus melaporkan kesalahan alih-alih menguji apakah $ a adalah a|b
string, sementara [[ $a =~ a|b ]]
tidak mengembalikan kesalahan.
Satu-satunya alasan adalah bahwa |
umumnya (di luar dan di dalam [[ ... ]]
) karakter khusus. Di [[ $a =
posisi itu, bash
mengharapkan jenis token yang merupakan KATA normal seperti argumen atau target pengalihan dalam baris perintah shell normal (tetapi seolah-olah extglob
opsi telah diaktifkan sejak bash 4.1).
(oleh WORD di sini, saya merujuk pada a kata dalam tata bahasa shell hipotetis seperti yang dijelaskan oleh spesifikasi POSIX , itu adalah sesuatu yang shell akan parse sebagai salah satu token dalam baris perintah shell sederhana, bukan definisi lain dari kata-kata seperti bahasa Inggris salah satu dari urutan huruf atau urutan karakter non-spasi. foo"bar baz"
, $(echo x y)
, dua seperti WORD s).
Dalam baris perintah shell normal:
echo a|b
Apakah echo a
disalurkan ke b
. a|b
bukan KATA , itu tiga token: aa
KATA , |
token dan b
KATA token.
Saat digunakan di [[ $a = a|b ]]
, bash
mengharapkan WORD yang didapat ( a
), tetapi kemudian menemukan |
token yang tidak terduga yang menyebabkan kesalahan.
Menariknya, bash
tidak mengeluh dalam:
[[ $a = a||b ]]
Karena sekarang a a
token diikuti oleh ||
token diikuti oleh b
, jadi diuraikan dengan cara yang sama seperti:
[[ $a = a || b ]]
Yang sedang menguji bahwa $a
adalah a
atau bahwa b
string non-kosong.
Sekarang di:
[[ $a =~ a|b ]]
bash
tidak dapat memiliki aturan penguraian yang sama. Memiliki aturan penguraian yang sama akan berarti bahwa di atas akan memberikan kesalahan dan bahwa seseorang perlu mengutip bahwa |
untuk memastikan a|b
adalah tunggal KATA . Tapi, sejak bash 3.2, jika Anda melakukannya:
[[ $a =~ 'a|b' ]]
Itu tidak lagi cocok dengan a|b
regexp tetapi terhadap a\|b
regexp. Artinya, mengutip shell memiliki efek samping menghilangkan makna khusus dari operator regexp. Ini fitur, jadi perilakunya mirip dengan yang [[ $a = "?" ]]
ada, tetapi pola wildcard (digunakan dalam[[ $a = pattern ]]
) shell WORDS (digunakan dalam gumpalan misalnya), sedangkan regexps tidak.
Jadi bash
harus memperlakukan semua operator regexp yang diperluas yang biasanya karakter shell khusus seperti|
, (
, )
berbeda ketika parsing argumen dari =~
operator.
Tetap, perhatikan itu sementara
[[ $a =~ (ab)*c ]]
sekarang bekerja,
[[ $a =~ [)}] ]]
tidak. Anda membutuhkan:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
Yang dalam versi sebelumnya bash
salah cocok dengan backslash. Yang itu sudah diperbaiki, tapi
[[ $a =~ [^]')'] ]]
Apakah tidak cocok di backslash seperti seharusnya misalnya. Karena bash
gagal menyadari bahwa )
ada di dalam kurung, maka lolos )
ke menghasilkan [^]\)]
regexp yang cocok dengan karakter apa pun tetapi ]
,\
dan )
.
ksh93
memiliki bug jauh lebih buruk di bagian depan itu.
Dalam zsh
, itu adalah kata shell normal yang diharapkan dan mengutip operator regexp tidak mempengaruhi arti dari operator regexp.
[[ $a =~ 'a|b' ]]
Cocok dengan a|b
Cocok regexp.
Itu berarti =~
dapat juga ditambahkan ke [
/ test
perintah:
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(Juga bekerja di yash
. =~
Kebutuhan dikutip dalamzsh
seperti =something
operator shell khusus di sana).
bash 3.1 dulu berperilaku seperti zsh
. Itu berubah di 3.2, mungkin untuk menyelaraskan dengan ksh93
(meskipun bash
shell yang pertama kali muncul dengan [[ =~ ]]
), tetapi Anda masih bisa melakukan BASH_COMPAT=31
atau shopt -s compat31
kembali ke perilaku sebelumnya (kecuali bahwa sementara [[ $a =~ a|b ]]
akan mengembalikan kesalahan dalam bash
3.1, itu tidak lagi dibash -O compat31
dengan versi yang lebih baru bash
).
Semoga ini menjelaskan mengapa saya mengatakan aturannya membingungkan dan mengapa menggunakan:
[[ $a =~ $var ]]
membantu termasuk dengan portabilitas ke shell lain.
|
khusus) diaktifkan secara default di sisi kanan[[ $var = $pattern ]]
. Akan menarik untuk mengisolasishopt
konfigurasi versi dan opsi di mana perilaku ini terlihat - jika hanya ituextglob
yang aktif, baik dengan konfigurasi default atau eksplisit, well, kita ada di sana.