Apa perbedaan antara tanda kurung ganda dan tunggal dalam bash?

427

Saya hanya ingin tahu apa sebenarnya perbedaan antara keduanya

[[ $STRING != foo ]]

dan

[ $STRING != foo ]

adalah, selain dari yang terakhir ini adalah posix-compliant, ditemukan di sh dan yang pertama adalah ekstensi yang ditemukan di bash.

0x89
sumber
1
Jika Anda juga bertanya-tanya tentang tidak menggunakan tanda kurung sama sekali, misalnya dalam konteks ifpernyataan, lihat mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev
juga, Dari dokumen Ubuntu: wiki.ubuntu.com/...
radistao

Jawaban:

310

Ada beberapa perbedaan. Menurut pendapat saya, beberapa yang paling penting adalah:

  1. [adalah builtin di Bash dan banyak kerang modern lainnya. Builtin [mirip dengan testdengan persyaratan tambahan penutupan ]. Buildin [dan testmeniru fungsi /bin/[dan /bin/testbersama dengan keterbatasan mereka sehingga skrip akan kompatibel. Executable asli masih ada sebagian besar untuk kepatuhan POSIX dan kompatibilitas mundur. Menjalankan perintah type [di Bash menunjukkan bahwa [diartikan sebagai builtin secara default. (Catatan: which [hanya mencari executable di PATH dan setara dengan type -p [)
  2. [[tidak kompatibel, itu tidak akan selalu berfungsi dengan /bin/shpoin apa pun . Jadi [[adalah pilihan Bash / Zsh / Ksh yang lebih modern.
  3. Karena [[dibangun ke dalam shell dan tidak memiliki persyaratan warisan, Anda tidak perlu khawatir tentang pemisahan kata berdasarkan variabel IFS untuk mengacaukan variabel yang mengevaluasi string dengan spasi. Oleh karena itu, Anda tidak benar-benar perlu memasukkan variabel dalam tanda kutip ganda.

Sebagian besar, sisanya hanya beberapa sintaks yang lebih bagus. Untuk melihat lebih banyak perbedaan, saya merekomendasikan tautan ini ke jawaban FAQ: Apa perbedaan antara tes, [dan [[? . Bahkan, jika Anda serius tentang skrip bash, saya sarankan membaca seluruh wiki , termasuk FAQ, Perangkap , dan Panduan. Bagian uji dari bagian panduan menjelaskan perbedaan-perbedaan ini juga, dan mengapa menurut penulis [[pilihan yang lebih baik jika Anda tidak perlu khawatir menjadi portabel. Alasan utamanya adalah:

  1. Anda tidak perlu khawatir untuk mengutip sisi kiri tes sehingga benar-benar dibaca sebagai variabel.
  2. Anda tidak perlu melarikan diri kurang dari dan lebih besar dari < >dengan garis miring terbalik agar mereka tidak dievaluasi sebagai pengalihan input, yang benar-benar dapat mengacaukan beberapa hal dengan menimpa file. Ini lagi kembali [[menjadi builtin. Jika [(tes) adalah program eksternal shell harus membuat pengecualian dalam cara mengevaluasi <dan >hanya jika /bin/testdipanggil, yang tidak akan masuk akal.
Kyle Brandt
sumber
5
Terima kasih, tautan ke bash FAQ adalah yang saya cari (tidak tahu tentang halaman itu, terima kasih).
0x89
2
Saya mengedit posting Anda dengan informasi ini, tetapi [dan pengujian dijalankan sebagai bawaan. Buildin dirancang untuk menggantikan / bin / [dan / bin / test tetapi perlu mereproduksi batasan binari juga. Perintah 'ketik [' memverifikasi bahwa builtin digunakan. 'yang [' hanya mencari yang dapat dieksekusi di PATH dan setara dengan 'ketik-P ['
klynch
133

Pendeknya:

[adalah Builtin bash

[[]] adalah Kata Kunci bash

Kata kunci: Kata kunci sangat mirip builtin, tetapi perbedaan utama adalah bahwa aturan penguraian khusus berlaku untuk mereka. Misalnya, [adalah built-in bash, sementara [[adalah kata kunci bash. Keduanya digunakan untuk menguji hal-hal, tetapi karena [[adalah kata kunci dan bukan builtin, itu mendapat manfaat dari beberapa aturan penguraian khusus yang membuatnya jauh lebih mudah:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Contoh pertama mengembalikan kesalahan karena bash mencoba mengalihkan file b ke perintah [a]. Contoh kedua sebenarnya melakukan apa yang Anda harapkan. Karakter <tidak lagi memiliki arti khusus dari operator Pengalihan File.

Sumber: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

abhiomkar
sumber
3
[adalah perintah shell POSIX; tidak perlu dibangun. ]Hanya argumen yang dicari oleh perintah itu, sehingga sintaksnya seimbang. Perintah ini adalah sinonim untuk testkecuali yang testtidak mencari penutupan ].
Kaz
81

Perbedaan perilaku

Beberapa perbedaan pada 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, <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 =~.

      Dalam Bashese: [adalah perintah bawaan , dan [[merupakan kata kunci: https://askubuntu.com/questions/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 by
    • [ a = a ] && [ b = b ]: POSIX dan setara yang dapat diandalkan
  • (

    • [[ (a = a || a = b) && a = b ]]: Salah
    • [ ( a = a ) ]: kesalahan sintaksis, ()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? ]]: true, POSIX memperpanjang kecocokan ekspresi reguler , ?tidak glob berkembang
    • [ a =~ a ]: kesalahan sintaks. Tidak 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
Cara menggunakan printf 'ab' | grep -Eq 'ab?'dalam if [ … ]?
meeDamian
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []adalah perintah sama seperti grep. The ()mungkin tidak diperlukan pada perintah yang saya tidak yakin: Saya menambahkan itu karena |, tergantung pada bagaimana Bash mem-parsing hal. Jika tidak ada |saya yakin Anda bisa menulis saja if cmd arg arg; then.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
@meeDamian ya, ()sepertinya tidak perlu : stackoverflow.com/questions/8965509/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Daftar bagus! Lihat juga: wiki.ubuntu.com/…
radistao
5

Single Bracket yaitu []POSIX shell compliant untuk melampirkan ekspresi kondisional.

Kurung Ganda yaitu [[]]versi yang ditingkatkan (atau ekstensi) dari versi POSIX standar, ini didukung oleh bash dan cangkang lainnya (zsh, ksh).

Di bash, untuk perbandingan numerik yang kami gunakan eq, ne, ltdan gt, dengan kurung ganda untuk perbandingan bisa kita gunakan ==, !=, <,dan >secara harfiah.

  • [adalah sinonim untuk perintah tes. Bahkan jika itu dibangun ke shell itu menciptakan proses baru.
  • [[ adalah versi baru yang disempurnakan, yang merupakan kata kunci, bukan program.

sebagai contoh:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works
Premraj
sumber
4

Berdasarkan pada bacaan cepat dari bagian yang relevan dari halaman manual, perbedaan utama tampaknya adalah bahwa ==dan !=operator cocok dengan suatu pola, daripada string literal, dan juga bahwa ada =~operator perbandingan regex.

womble
sumber