Dulu saya yakin tentang fakta bahwa mengutip string selalu merupakan praktik yang baik untuk menghindari shell mengurai itu.
Lalu saya menemukan ini:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Mencoba mengisolasi masalah, mendapatkan kesalahan yang sama:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Saya memecahkan masalah seperti ini:
[ "$x" = '1' ] && [ "$y" = '1' ]
Saya masih perlu tahu apa yang terjadi di sini.
[[ "$x" = '1' && "$y" = '1' ]]
-a
dan-o
sebagai usang untuk alasan ini (yang merupakan[OB]
superskrip di samping spesifikasi mereka). Jika Anda menulis[ "$x" = 1 ] && [ "$y" = 1 ]
sebagai gantinya, Anda akan baik-baik saja, dan akan berada dalam ranah perilaku yang terdefinisi dengan baik / standar.[ "x$x" = "x1" ]
untuk mencegah argumen disalahartikan sebagai operator.dash
daripada Bash, itu masih merupakan praktik yang bermanfaat.Jawaban:
Ini adalah kasus sudut yang sangat tidak jelas yang dapat dianggap sebagai bug dalam cara tes
[
bawaan ditetapkan; Namun, itu cocok dengan perilaku[
biner aktual yang tersedia di banyak sistem. Sejauh yang saya tahu, itu hanya mempengaruhi kasus-kasus tertentu dan variabel yang memiliki nilai yang cocok dengan[
operator yang seperti(
,!
,=
,-e
, dan sebagainya.Biarkan saya jelaskan alasannya, dan bagaimana cara mengatasinya dalam shell Bash dan POSIX.
Penjelasan:
Pertimbangkan yang berikut ini:
Tidak masalah; hasil di atas tidak ada kesalahan, dan output
yes
. Beginilah cara kami mengharapkan barang bekerja. Anda dapat mengubah string perbandingan menjadi'1'
jika Anda suka, dan nilainyax
, dan itu akan berfungsi seperti yang diharapkan.Perhatikan bahwa
/usr/bin/[
biner yang sebenarnya berperilaku dengan cara yang sama. Jika Anda menjalankan mis.'/usr/bin/[' '(' = '(' ']'
Tidak ada kesalahan, karena program dapat mendeteksi bahwa argumen terdiri dari operasi perbandingan string tunggal.Bug terjadi ketika kita dan dengan ekspresi kedua. Tidak masalah apa ekspresi kedua, asalkan valid. Sebagai contoh,
output
yes
, dan jelas merupakan ekspresi yang valid; tapi, jika kita gabungkan keduanya,Bash menolak ekspresi jika dan hanya jika
x
ada(
atau!
.Jika kita menjalankan di atas menggunakan
[
program yang sebenarnya , yaitukesalahan akan dapat dimengerti: karena shell melakukan substitusi variabel,
/usr/bin/[
biner hanya menerima parameter(
=
(
-a
1
=
1
dan terminating]
, itu dimengerti gagal untuk menguraikan apakah tanda kurung buka memulai sub-ekspresi atau tidak, ada dan operasi yang terlibat. Tentu, menguraikannya sebagai dua perbandingan string adalah mungkin, tetapi melakukannya dengan rakus seperti itu dapat menyebabkan masalah ketika diterapkan pada ekspresi yang tepat dengan sub-ekspresi yang diurung.Masalahnya, sebenarnya, shell
[
built-in berperilaku dengan cara yang sama, seolah-olah itu memperluas nilaix
sebelum memeriksa ekspresi.(Ambiguitas ini, dan yang lainnya terkait dengan ekspansi variabel, adalah alasan besar mengapa Bash diimplementasikan dan sekarang merekomendasikan penggunaan
[[ ... ]]
ekspresi tes sebagai gantinya.)Penanganannya sepele, dan sering terlihat dalam skrip menggunakan
sh
shell yang lebih lama . Anda menambahkan karakter "aman", seringx
, di depan string (kedua nilai dibandingkan), untuk memastikan ekspresi diakui sebagai perbandingan string:sumber
[
bug bawaan. Jika ada, itu adalah cacat desain yang melekat.[[
adalah kata kunci shell , bukan hanya perintah bawaan, sehingga ia dapat melihat hal-hal sebelum penghapusan kutipan, dan benar-benar menimpa pemisahan kata yang biasa. mis.[[ $x == 1 ]]
tidak perlu menggunakan"$x"
, karena[[
konteksnya berbeda dari normal. Lagi pula, ini adalah bagaimana[[
bisa menghindari jebakan[
. POSIX perlu[
berperilaku seperti itu, dan bash sebagian besar adalah POSIX compliant bahkan tanpa--posix
, jadi mengubah[
menjadi kata kunci tidak menarik.[
.[
perilaku yang dibebani oleh sejarah sebelum POSIX, saya memilih kata yang terakhir sebagai gantinya.) Saya pribadi menghindari menggunakan[[
dalam contoh skrip saya, tetapi hanya karena itu adalah pengecualian terhadap rekomendasi mengutip yang selalu saya bahas (karena menghilangkan kutipan adalah alasan paling umum untuk bug skrip yang saya lihat), dan saya belum memikirkan paragraf sederhana untuk menjelaskan mengapa[[
pengecualian pada aturan, tanpa membuat rekomendasi kutipan saya tersangka.sh
skrip, mendahului standardisasi POSIX. Menggunakan subekspresi tanda kurung dan-a
dan-o
operator logis dalam pengujian /[
lebih efisien yang bergantung pada perangkaian ekspresi (via&&
dan||
); hanya saja pada mesin saat ini, perbedaannya tidak relevan.&&
dan menggunakan||
, tetapi alasannya, itu membuat kita manusia lebih mudah untuk memelihara (membaca, memahami, dan memodifikasi jika / bila perlu) mereka tanpa memperkenalkan bug. Jadi, saya tidak mengkritik saran Anda, tetapi hanya alasan di balik saran itu. (Untuk alasan yang sangat mirip, yang.LT.
,.GE.
, dll operator perbandingan dalam FORTRAN 77 mendapat versi yang lebih ramah manusia<
,>=
dll di versi.)[
aliastest
melihat:test
menerima subekspresi dalam tanda kurung; jadi ia berpikir bahwa kurung kurawal membuka subekspresi dan mencoba menguraikannya; pengurai melihat=
sebagai hal pertama dalam subekspresi dan berpikir bahwa itu adalah tes panjang-string implisit, jadi itu menyenangkan; subexpression yang kemudian harus diikuti dengan kurung kanan, dan sebaliknya parser menemukan1
bukan)
. Dan itu mengeluh.Ketika
test
memiliki tepat tiga argumen, dan argumen tengah adalah salah satu operator yang diakui, itu berlaku operator untuk argumen 1 dan 3 tanpa mencari subekspresi dalam tanda kurung.Untuk rincian lengkap melihat
man bash
, mencaritest expr
.Kesimpulan: Algoritma parsing yang digunakan oleh
test
rumit. Gunakan hanya ekspresi sederhana dan gunakan operator shell!
,&&
dan||
untuk menggabungkannya.sumber