Apa perbedaan antara [[$ a == z *]] dan [$ a == z *]?

35

Apakah ada perbedaan di antara keduanya.

[[ $a == z* ]]

dan

[ $a == z* ] 

Bisakah saya memiliki contoh di mana mereka akan memiliki keluaran yang berbeda?

Lebih jauh, bagaimana cara kerjanya [[ ]]berbeda [ ]?

munish
sumber

Jawaban:

41

Perbedaan antara [[ … ]]dan [ … ]sebagian besar tercakup dalam menggunakan braket tunggal atau ganda - bash . Yang terpenting, [[ … ]]adalah sintaks khusus, sedangkan [nama yang tampak lucu untuk sebuah perintah. [[ … ]]memiliki aturan sintaksis khusus untuk apa yang ada di dalamnya, [ … ]tidak.

Dengan kerut tambahan wildcard, berikut cara [[ $a == z* ]]dievaluasi:

  1. Pisahkan perintah: ini adalah [[ … ]]konstruksi kondisional di sekitar ekspresi kondisional $a == z*.
  2. Parsing ekspresi kondisional: ini adalah ==operator biner, dengan operan $adan z*.
  3. Perluas operan pertama ke dalam nilai variabel a.
  4. Mengevaluasi ==operator: menguji apakah nilai variabel acocok dengan pola z*.
  5. Mengevaluasi ekspresi kondisional: hasilnya adalah hasil dari operator kondisional.
  6. Perintah sekarang dievaluasi, statusnya adalah 0 jika ekspresi kondisional benar dan 1 jika itu salah.

Inilah cara [ $a == z* ]dievaluasi:

  1. Mengurai perintah: ini adalah [perintah dengan argumen yang dibentuk dengan mengevaluasi kata-kata $a, ==, z*, ].
  2. Perluas $anilai variabel a.
  3. Lakukan pemisahan kata dan pembuatan nama file pada parameter perintah.
    • Misalnya, jika nilai aadalah string 6 karakter foo b*(yang diperoleh misalnya a='foo b*') dan daftar file dalam direktori saat ini ( bar, baz, qux, zim, zum), maka hasil dari ekspansi adalah daftar kata berikut: [, foo, bar, baz, ==, zim, zum, ].
  4. Jalankan perintah [dengan parameter yang diperoleh pada langkah sebelumnya.
    • Dengan nilai contoh di atas, [perintah mengeluhkan kesalahan sintaks dan mengembalikan status 2.

Catatan: Di [[ $a == z* ]], pada langkah 3, nilai atidak mengalami pemisahan kata dan pembuatan nama file, karena itu dalam konteks di mana satu kata diharapkan (argumen sebelah kiri dari operator kondisional ==). Dalam kebanyakan kasus, jika satu kata masuk akal pada posisi itu maka ekspansi variabel berperilaku seperti itu dalam tanda kutip ganda. Namun, ada pengecualian untuk aturan itu: di [[ abc == $a ]], jika nilai aberisi wildcard, maka adicocokkan dengan pola wildcard. Sebagai contoh, jika nilai ais a*maka [[ abc == $a ]]benar (karena wildcard *berasal dari perluasan $apertandingan yang tidak dikutip *) sedangkan [[ abc == "$a" ]]false (karena karakter biasa*berasal dari ekspansi yang dikutip $atidak cocok bc). Di dalam [[ … ]], tanda kutip ganda tidak membuat perbedaan, kecuali di sisi kanan operator pencocokan string ( =, ==, !=dan =~).

Gilles 'SANGAT berhenti menjadi jahat'
sumber
39

[adalah alias untuk testperintah. Unix Versi 6 memiliki ifperintah, tetapi Versi 7 (1979) datang dengan shell Bourne baru yang memiliki beberapa konstruksi pemrograman termasuk if-then-else-elif-fi, dan Unix versi 7 menambahkan testperintah yang melakukan sebagian besar "tes" yang dilakukan oleh ifperintah di versi yang lebih lama.

[dibuat alias untuk testdan keduanya dibuat dibangun ke shell di Unix System III (1981) . Meskipun perlu dicatat bahwa beberapa varian Unix tidak memiliki [perintah sampai kemudian setelah itu ( hingga awal 2000-an pada beberapa BSD di mana shdidasarkan pada cangkang Almquist (sebuah testbuiltin selalu dimasukkan dalam ashsumber, tetapi pada mereka BSD awalnya dinonaktifkan)).

Perhatikan bahwa testalias [adalah perintah untuk melakukan "tes", tidak ada penugasan yang dilakukan oleh perintah itu, jadi tidak ada alasan untuk menyatukan antara penugasan dan operator kesetaraan, jadi operator kesetaraan itu =. ==hanya didukung oleh beberapa implementasi terbaru dari [(dan hanya alias untuk =).

Karena [tidak lebih dari sebuah perintah, itu diurai dengan cara yang sama seperti perintah lain oleh shell.

Khususnya, dalam contoh Anda $a, karena tidak dikutip, akan dipecah menjadi beberapa kata sesuai dengan aturan pemisahan kata yang biasa, dan setiap kata akan mengalami generasi nama file alias globbing untuk menghasilkan lebih banyak kata, masing-masing kata tersebut menghasilkan argumen terpisah untuk [perintah.

Demikian pula, z*akan diperluas ke daftar nama file di direktori saat ini dimulai dengan z.

Jadi misalnya, jika $aini b* = x, dan ada z1, z2, b1dan b2file dalam direktori saat ini, The [perintah akan mendapatkan 9 argumen: [, b1, b2, =, x, ==, z1, z2dan ].

[mem-parsing argumennya sebagai ekspresi kondisional. 9 argumen itu tidak menambahkan hingga ekspresi kondisional yang valid, jadi itu mungkin akan mengembalikan kesalahan.

The [[ ... ]]konstruk diperkenalkan oleh Korn shell mungkin sekitar tahun 1988 sebagai ksh86atahun 1987 tidak memilikinya sementara ksh88memiliki itu dari awal.

Selain ksh (semua implementasi), [[...]]juga didukung oleh bash (karena versi 2.02) dan zsh, tetapi ketiga implementasi berbeda dan ada perbedaan antara setiap versi dari shell yang sama meskipun perubahan umumnya kompatibel ke belakang (pengecualian yang terkenal adalah bash's =~operator yang telah dikenal untuk memecahkan beberapa skrip setelah versi tertentu ketika perilakunya berubah). [[...]]tidak ditentukan oleh POSIX, atau Unix atau Linux (LSB). Ini telah dipertimbangkan untuk dimasukkan beberapa kali, tetapi tidak dimasukkan sebagai fungsi umum dari itu didukung oleh shell utama sudah dicakup oleh [perintah dan case-in-esackonstruk.

Seluruh [[ ... ]]konstruk membentuk sebuah perintah. Artinya, ia memiliki status keluar (yang merupakan aset paling penting karena merupakan hasil dari evaluasi ekspresi bersyarat), Anda dapat mengirimnya ke perintah lain (meskipun itu tidak akan berguna), dan umumnya menggunakannya di mana pun Anda mau gunakan perintah lain (hanya di dalam shell, karena ini adalah shell konstruksi) tetapi tidak diurai seperti perintah sederhana yang normal. Apa yang ada di dalamnya diuraikan oleh shell sebagai ekspresi kondisional dan aturan umum pemisahan kata dan pembuatan nama file berlaku berbeda.

[[ ... ]]tidak tahu tentang ==dari awal dan setara dengan =1 . Kesalahan ksh (dan menyebabkan kebingungan dan banyak bug) adalah bahwa =dan ==bukan operator kesetaraan tetapi operator pencocokan pola (meskipun aspek pencocokan dapat dinonaktifkan dengan mengutip tetapi dengan aturan tidak jelas yang berbeda dari shell ke shell).

Dalam kode Anda di atas [[ $a == z* ]], shell akan menguraikannya menjadi beberapa token dalam aturan yang mirip dengan yang biasa, mengenalinya sebagai perbandingan pola yang cocok, memperlakukan z*sebagai pola untuk mencocokkan dengan konten avariabel.

Secara umum, lebih sulit menembak diri sendiri dengan kaki [[ ... ]]daripada menggunakan [perintah. Tetapi beberapa aturan suka

  • selalu mengutip variabel
  • jangan pernah gunakan operator -aatau -o(gunakan beberapa [perintah dan operator &&dan || shell )

Dapat [diandalkan dengan cangkang POSIX.

[[...]]dalam shell yang berbeda mendukung operator tambahan seperti -nt, operator pencocokan regexp ... tetapi daftar dan perilaku bervariasi dari shell ke shell dan versi ke versi.

Jadi, kecuali Anda tahu apa shell dan versi minimum dari itu skrip Anda akan pernah ditafsirkan, mungkin lebih aman untuk tetap dengan [perintah standar .


1 Pengecualian: [[...]]telah ditambahkan ke versi bash 2.02. Sampai 2.03dimana itu diubah, [[ x = '?' ]]akan mengembalikan true sementara [[ x == '?' ]]akan mengembalikan false. Mengutip tidak mencegah pencocokan pola saat menggunakan =operator di versi tersebut, tetapi melakukannya saat menggunakan ==.

Stéphane Chazelas
sumber
Info bagus Bisakah Anda jelaskan mengapa "tidak pernah menggunakan operator -a atau -o"?
grebneke
@grebneke, coba [ '!' = foo -o a = a ]di bashmisalnya.
Stéphane Chazelas
0

keduanya digunakan untuk mengevaluasi ekspresi dan [[tidak akan bekerja dengan shell bourn POSIX yang lebih lama dan [[juga mendukung pencocokan pola dan regex. contoh coba ini

[ $n -eq 0 -a $y -eq 0 ] && echo "Error" || echo "Ok"

[[ $n -eq 0 && $y -eq 0 ]] && echo "Error" || echo "Ok"

harish.venkat
sumber
Perhatikan bahwa shell Bourne bukan POSIX, [[...]]hanya mendukung regexps di beberapa versi beberapa shell, seperti halnya beberapa versi [yang mendukung pencocokan regexp. Untuk perbandingan aritemik, dalam cangkang yang mendukung [[, Anda lebih suka menulis(( n == 0 && y == 0))
Stéphane Chazelas