Perbedaan antara tanda kurung kotak tunggal dan ganda di Bash

161

Saya membaca contoh bash tentang iftetapi beberapa contoh ditulis dengan tanda kurung tunggal:

if [ -f $param ]
then
  #...
fi

lainnya dengan tanda kurung ganda:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Apa bedanya?

rkmax
sumber
3
Anda bisa mendapatkan jawaban Anda dengan melihat jawaban dari pertanyaan ini: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Jawaban:

188

Tunggal []adalah tes kondisi yang sesuai dengan posix shell.

Double [[]]adalah ekstensi ke standar []dan didukung oleh bash dan shell lainnya (misalnya zsh, ksh). Mereka mendukung operasi tambahan (serta operasi posix standar). Misalnya: ||alih-alih -odan mencocokkan regex dengan =~. Daftar perbedaan yang lebih lengkap dapat ditemukan di bagian manual bash pada konstruksi kondisional .

Gunakan []kapan pun Anda ingin skrip Anda portabel di seluruh shell. Gunakan [[]]jika Anda ingin ekspresi kondisional tidak didukung oleh []dan tidak perlu portabel.

cmh
sumber
6
Saya akan menambahkan bahwa jika skrip Anda tidak dimulai dengan shebang yang secara eksplisit meminta shell yang mendukung [[ ]](misalnya bash with #!/bin/bashatau #!/usr/bin/env bash), Anda harus menggunakan opsi portabel. Skrip yang berasumsi / bin / sh mendukung ekstensi seperti ini akan merusak OS seperti rilis Debian dan Ubuntu baru-baru ini di mana bukan itu masalahnya.
Gordon Davisson
87

Perbedaan perilaku

Diuji dalam Bash 4.3.11:

  • Ekstensi POSIX vs Bash:

  • perintah reguler vs sihir

    • [ hanyalah perintah biasa dengan nama yang aneh.

      ]hanyalah argumen [yang mencegah argumen lebih lanjut digunakan.

      Ubuntu 16.04 sebenarnya memiliki executable yang /usr/bin/[disediakan oleh coreutils, tetapi versi built-in bash lebih diutamakan.

      Tidak ada yang diubah dalam cara Bash mem-parsing perintah.

      Secara khusus, <adalah pengalihan, &&dan ||menggabungkan beberapa perintah, ( )menghasilkan subkulit kecuali lolos \, dan perluasan kata terjadi seperti biasa.

    • [[ X ]]adalah konstruksi tunggal yang membuat Xdiurai secara ajaib. <, &&, ||Dan ()diperlakukan khusus, dan aturan kata membelah berbeda.

      Ada juga perbedaan lebih lanjut seperti =dan =~.

      Di Bashese: [adalah perintah bawaan , dan [[merupakan kata kunci: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && dan ||

    • [[ a = a && b = b ]]: benar, logis dan
    • [ a = a && b = b ]: kesalahan sintaks, &&diurai sebagai pemisah perintah ANDcmd1 && cmd2
    • [ a = a -a b = b ]: setara, tetapi ditinggalkan oleh POSIX³
    • [ a = a ] && [ b = b ]: POSIX dan setara yang dapat diandalkan
  • (

    • [[ (a = a || a = b) && a = b ]]: Salah
    • [ ( a = a ) ]: kesalahan sintaks, ()ditafsirkan sebagai subkulit
    • [ \( a = a -o a = b \) -a a = b ]: ekuivalen, tetapi ()ditinggalkan oleh POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]Setara dengan POSIX 5
  • pemisahan kata dan pembuatan nama file berdasarkan ekspansi (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: true, kutipan tidak diperlukan
    • x='a b'; [ $x = 'a b' ]: kesalahan sintaks, diperluas ke [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: kesalahan sintaks jika ada lebih dari satu file di direktori saat ini.
    • x='a b'; [ "$x" = 'a b' ]: Setara POSIX
  • =

    • [[ ab = a? ]]: benar, karena tidak cocok dengan pola ( * ? [adalah sihir). Tidak glob berkembang ke file dalam direktori saat ini.
    • [ ab = a? ]: a?glob berkembang. Jadi mungkin benar atau salah tergantung pada file di direktori saat ini.
    • [ ab = a\? ]: false, bukan ekspansi glob
    • =dan ==sama di keduanya [dan [[, tetapi ==merupakan ekstensi Bash.
    • case ab in (a?) echo match; esac: Setara POSIX
    • [[ ab =~ 'ab?' ]]: false 4 , kehilangan sihir dengan''
    • [[ ab? =~ 'ab?' ]]: benar
  • =~

    • [[ ab =~ ab? ]]: benar, POSIX memperpanjang kecocokan ekspresi reguler , ?tidak glob berkembang
    • [ a =~ a ]: kesalahan sintaks. Tidak ada yang setara dengan bash.
    • printf 'ab\n' | grep -Eq 'ab?': Setara POSIX (hanya data garis tunggal)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Setara POSIX.

Rekomendasi : selalu gunakan [].

Ada setara POSIX untuk setiap [[ ]]konstruk yang pernah saya lihat.

Jika Anda menggunakan [[ ]]Anda:

  • kehilangan portabilitas
  • memaksa pembaca untuk mempelajari seluk-beluk ekstensi bash yang lain. [hanyalah perintah biasa dengan nama yang aneh, tidak ada semantik khusus yang terlibat.

¹ Terinspirasi dari [[...]]konstruksi yang setara di shell Korn

² tetapi gagal untuk beberapa nilai aatau b(suka +atau index) dan melakukan perbandingan numerik jika adan bterlihat seperti bilangan bulat desimal. expr "x$a" '<' "x$b"bekerja di sekitar keduanya.

³ dan juga gagal untuk beberapa nilai aatau bsuka !atau (.

4 di bash 3.2 dan di atas dan kompatibilitas yang disediakan untuk bash 3.1 tidak diaktifkan (seperti dengan BASH_COMPAT=3.1)

5 meskipun pengelompokan (di sini dengan {...;}kelompok perintah alih-alih (...)yang akan menjalankan subkulit yang tidak perlu) tidak diperlukan karena operator ||dan &&shell (sebagai lawan dari ||dan && [[...]]operator atau -o/ -a [operator) memiliki prioritas yang sama. Jadi [ a = a ] || [ a = b ] && [ a = b ]akan setara.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
3
" Ada padanan POSIX untuk setiap [[]] konstruksi yang pernah saya lihat. " Hal yang sama dapat dikatakan tentang bahasa Lengkap Turing di muka planet ini.
A. Rick
3
@ A.Rick itu akan menjadi jawaban yang valid untuk semua "Bagaimana melakukan X dalam bahasa Y" pertanyaan SO :-) Tentu saja, ada "nyaman" tersirat dalam pernyataan itu.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6
Ringkasan yang fantastis. Terima kasih atas usahanya. Namun saya tidak setuju dengan rekomendasi tersebut. Ini mudah dibawa versus sintaks yang lebih konsisten dan kuat. Jika Anda dapat meminta bash> 4 di lingkungan Anda, maka sintaks [[]] direkomendasikan.
Bernard
@Downvoters tolong jelaskan agar saya dapat mempelajari dan meningkatkan info :-)
Ciro Santilli 郝海东 冠状 病 六四 六四 事件
8
Jawaban yang bagus tapi saya pikir Rekomendasi : selalu gunakan[] harus dibaca sebagai preferensi saya : gunakan []jika Anda tidak ingin kehilangan portabilitas . Seperti yang dinyatakan di sini : Jika portabilitas / kesesuaian dengan POSIX atau BourneShell menjadi perhatian, sintaks lama harus digunakan. Jika di sisi lain skrip membutuhkan BASH, Zsh, atau KornShell, sintaks baru biasanya lebih fleksibel, tetapi tidak selalu kompatibel ke belakang. Saya lebih suka pergi [[ ab =~ ab? ]]jika saya bisa dan tidak memiliki kekhawatiran tentang kompatibilitas mundur daripadaprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo
14

Di dalam kurung tunggal untuk pengujian kondisi (yaitu [...]), beberapa operator seperti tunggal =didukung oleh semua shell, sedangkan penggunaan operator ==tidak didukung oleh beberapa shell yang lebih lama.

Di dalam tanda kurung ganda untuk pengujian kondisi (yaitu [[...]]), tidak ada perbedaan antara menggunakan =atau ==di shell yang lama atau yang baru.

Sunting: Saya juga harus mencatat bahwa: Dalam bash, selalu gunakan tanda kurung ganda [[...]] jika memungkinkan, karena lebih aman daripada tanda kurung tunggal. Saya akan menggambarkan mengapa dengan contoh berikut:

if [ $var == "hello" ]; then

jika $ var adalah null / kosong, maka inilah yang dilihat skrip:

if [ == "hello" ]; then

yang akan merusak skrip Anda. Solusinya adalah dengan menggunakan kurung ganda, atau selalu ingat untuk menempatkan tanda kutip di sekitar variabel Anda ( "$var"). Kurung ganda adalah praktik pengkodean defensif yang lebih baik.

sampson-chen
sumber
1
Menempatkan tanda kutip di sekitar semua pembacaan variabel kecuali Anda memiliki alasan yang sangat baik untuk tidak melakukannya adalah praktik pengkodean defensif yang jauh lebih baik, karena ini berlaku untuk semua pembacaan variabel, bukan hanya mereka yang dalam kondisi. Bug installer iTunes pernah menghapus file orang jika nama hard drive berisi spasi (atau sesuatu seperti itu). Ini juga memecahkan masalah yang Anda sebutkan.
Chai T. Rex
1

Anda dapat menggunakan tanda kurung ganda untuk pencocokan regex ringan, misalnya:

if [[ $1 =~ "foo.*bar" ]] ; then

(selama versi bash yang Anda gunakan mendukung sintaks ini)

asf107
sumber
6
Kecuali Anda telah mengutip polanya, jadi sekarang diperlakukan sebagai string literal.
ormaaj
sangat benar. kadang ini mengganggu saya :)
asf107
1

Bash manual mengatakan:

Saat digunakan dengan [[, operator '<' dan '>' mengurutkan secara leksikografis menggunakan lokal saat ini. Perintah tes menggunakan pemesanan ASCII.

(Perintah tes identik dengan [])

pengguna5500105
sumber