Mengapa `[` shell bawaan dan `[[` kata kunci shell?


64

Sejauh yang saya tahu, [[adalah versi yang disempurnakan [, tapi saya bingung ketika saya melihat [[sebagai kata kunci dan [ditampilkan sebagai builtin.

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDP berkata

Sebuah builtin mungkin merupakan sinonim dari perintah sistem dengan nama yang sama, tetapi Bash menambahkannya secara internal. Sebagai contoh, perintah echo Bash tidak sama dengan / bin / echo, meskipun perilaku mereka hampir identik.

dan

Kata kunci adalah kata, token, atau operator yang dicadangkan. Kata kunci memiliki arti khusus untuk shell, dan memang merupakan blok bangunan dari sintaks shell. Sebagai contoh, untuk, sementara, lakukan, dan! adalah kata kunci. Mirip dengan builtin, kata kunci dikodekan ke dalam Bash, tetapi tidak seperti builtin, kata kunci tidak dengan sendirinya merupakan perintah, tetapi subunit dari perintah yang dibangun. [2]

Bukankah itu berarti keduanya [dan [[kata kunci? Apakah ada sesuatu yang saya lewatkan di sini? Juga, tautan ini menegaskan kembali bahwa keduanya [dan [[harus memiliki jenis yang sama.



9
/ bin / [ada di komputer saya.
Joshua

2
Sebagai demonstrasi sederhana dari satu perbedaan antara kedua: if "[" $x -eq 3 ]bekerja seperti yang diharapkan (karena Bash mencari perintah yang disebut [, dan ini ada), tapi if "[[" $x -eq 3 ]]tidak tidak bekerja (karena sekali lagi Bash mencari perintah dari nama yang tepat, tetapi tidak ada [[perintah).
Kyle Strand

1
@ Yosua Begitu /usr/bin/echo, tapi itu tidak berarti itu bukan builtin .
Jonathon Reinhart

Semua bulitin yang juga ada argumen parse eksternal jika mereka tidak dibangun.
Joshua

Jawaban:


80

Perbedaan antara [dan [[sangat mendasar.

  • [adalah sebuah perintah. Argumennya diproses sama seperti argumen perintah lainnya diproses. Sebagai contoh, pertimbangkan:

    [ -z $name ]

    Shell akan memperluas $namedan melakukan kedua membelah kata dan generasi nama file pada hasil, hanya seperti itu untuk perintah lain.

    Sebagai contoh, berikut ini akan gagal:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments

    Agar ini berfungsi dengan benar, kutipan diperlukan:

    $ [ -n "$name" ] && echo not empty
    not empty
  • [[adalah kata kunci shell dan argumennya diproses sesuai dengan aturan khusus. Sebagai contoh, pertimbangkan:

    [[ -z $name ]]

    Shell akan memperluas $nametetapi, tidak seperti perintah lain, ia akan melakukan tidak membelah kata atau generasi nama file pada hasil. Misalnya, berikut ini akan berhasil meskipun ruang yang disematkan di name:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty

Ringkasan

[ adalah perintah dan tunduk pada aturan yang sama dengan semua perintah lain yang dijalankan shell.

Karena [[kata kunci, bukan perintah, shell memperlakukannya secara khusus dan beroperasi di bawah aturan yang sangat berbeda.


+1. Terima kasih. Bisakah Anda memberikan sumber (referensi) untuk apa aturan perintah dan kata kunci beroperasi?
Tim

@Tim Aturan di mana perintah beroperasi dijelaskan secara terperinci man bash. Lihat, khususnya, bagian yang berjudul "SIMPLE COMMAND EXPANSION" dan "COMMAND EXECUTION". Selain [[, bash lain kata kunci termasuk if, then, while, dan 'kasus'. Tidak ada aturan umum untuk kata kunci: setiap kata kunci adalah kasus khusus. man bash termasuk perincian untuk masing-masing.
John1024

62

Dalam V7 Unix - di mana shell Bourne memulai debutnya - [dipanggil test, dan itu hanya ada sebagai /bin/test. Jadi, kode yang Anda tulis hari ini adalah:

if [ "$foo" = "bar" ] ; then ...

Anda akan menulis sebagai

if test "$foo" = "bar" ; then ...

Notasi kedua ini masih ada, dan saya merasa lebih jelas tentang apa yang terjadi: Anda memanggil perintah yang memanggil testargumen yang mengevaluasi dan mengembalikan kode status keluar yang ifdigunakan untuk memutuskan apa yang harus dilakukan selanjutnya. Perintah itu dapat dibangun ke dalam shell, atau mungkin program eksternal.¹

[sebagai alternatif untuk testdatang kemudian .² Ini mungkin merupakan sinonim untuk builtin test, tetapi juga disediakan seperti /bin/[pada sistem modern untuk shell yang tidak memilikinya sebagai builtin.

[dan testdapat diimplementasikan menggunakan kode yang sama. Ini adalah kasus untuk /bin/[dan /bin/testpada OS X, di mana ini adalah tautan keras ke executable yang sama. Sebagai akibatnya, implementasi sepenuhnya mengabaikan trailing ]: itu tidak mengharuskannya jika Anda menyebutnya /bin/[, dan tidak mengeluh jika Anda lakukan memberikan kepada /bin/test.⁴

Tak satu pun dari sejarah itu yang memengaruhi [[, karena tidak pernah ada program primordial yang disebut [[. Itu ada murni di dalam shell yang mengimplementasikannya sebagai ekstensi ke shell POSIX .

Bagian dari perbedaan antara "bawaan" dan "kata kunci" adalah karena sejarah ini. Ini juga mencerminkan fakta bahwa aturan sintaks untuk [[ekspresi parsing berbeda, seperti yang ditunjukkan dalam jawaban John1024 .⁵


Catatan kaki:

  1. Ketika Anda melihatnya seperti itu, itu menjelaskan mengapa Anda harus meletakkan spasi di [dalam skrip shell, tidak seperti cara kurung dan kurung bekerja di sebagian besar bahasa pemrograman lainnya. Jika parser perintah shell diizinkan if["$x"..., itu juga harus mengizinkaniftest"$x"...

  2. Itu terjadi sekitar tahun 1980. /bin/[tidak ada dalam salinan Ancient Unix V7 saya dari tahun 1979, juga tidak man testmendokumentasikannya sebagai alias. Di halaman masuk man yang sesuai saya miliki dalam salinan pra-rilis dari Sistem III pengguna dari 1980, itu adalah terdaftar.

  3. ls -i /bin/[ /bin/test

  4. Tapi jangan mengandalkan perilaku ini. The Bash built-in versi [tidak memerlukan penutupan ], dan built-in yang testpelaksanaannya akan mengeluh jika Anda tidak memberikan itu.

  5. Perbedaan perintah builtin vs eksternal juga penting karena alasan lain: kedua implementasi mungkin berperilaku berbeda. Ini adalah kasus echopada banyak sistem. Karena hanya ada satu implementasi, tidak ada perbedaan yang perlu dibuat untuk kata kunci.


Terima kasih @Warren Young, tapi mengapa builtindan keywordperbedaan antara [dan [[ketika kedua menyediakan fungsi yang sama (kecuali kenyataan bahwa [[dilengkapi dengan fitur lebih [)?
Sree

2
@sree [[menjadi kata kunci memungkinkan bash untuk melakukan hal-hal yang tidak mungkin dilakukan [- misalnya mengutip tidak perlu banyak waktu, karena shell sadar bahwa itu adalah variabel. Itu mengatakan, pemrosesan baris perintah terpengaruh ketika kata kunci digunakan, tetapi tidak ketika builtin digunakan - itu terjadi jauh kemudian.
muru

1
Perhatikan bahwa kode untuk shell Bourne V7 menunjukkan [builtin, tetapi kode tersebut dikomentari.
Stéphane Chazelas

cdadalah builtin, tetapi tidak membayangi apa pun ( cdtidak dapat diimplementasikan sebagai program eksternal).
Paŭlo Ebermann

2
Ini adalah ringkasan sejarah yang sangat baik, tetapi tidak menyertakan detail penting (IMO), ditunjukkan oleh muru di atas dan oleh John1024 dalam jawaban mereka, bahwa membuat [[kata kunci memungkinkan shell untuk menggunakan aturan penguraian khusus untuk argumennya. Jadi, sayangnya, upvote saya masuk ke John1024.
Ilmari Karonen

3

[awalnya hanya perintah eksternal, nama lain untuk /bin/test. Tetapi beberapa perintah, seperti [dan echo, digunakan begitu sering dalam skrip shell sehingga shell implementors memutuskan untuk menyalin kode langsung ke shell itu sendiri, daripada harus menjalankan proses lain setiap kali mereka digunakan. Itu mengubah perintah-perintah ini menjadi "bawaan", meskipun Anda masih dapat menjalankan program eksternal melalui path lengkapnya.

[[datang jauh kemudian. Meskipun builtin diimplementasikan secara internal di dalam shell, itu diurai seperti perintah eksternal. Seperti yang dijelaskan dalam jawaban John1024, ini berarti bahwa variabel yang tidak dikutip akan menyelesaikan pemisahan kata, dan token menyukai >dan <diproses sebagai pengalihan I / O. Ini membuat penulisan perbandingan perbandingan yang rumit menjadi tidak nyaman. [[dibuat sebagai sintaksis shell, sehingga bisa diurai secara ideosinkratis. Dalam [[variabel tidak mendapatkan pemisahan kata, <dan >dapat digunakan sebagai operator pembanding, =dapat berperilaku berbeda tergantung pada apakah parameter berikutnya dikutip atau tidak, dll. Ini semua adalah kemudahan yang membuat [[lebih mudah digunakan daripada [perintah / builtin tradisional .

Mereka tidak bisa hanya membaca ulang [sebagai sintaksis seperti ini karena itu akan menjadi perubahan yang tidak kompatibel ke jutaan skrip. Dengan menggunakan [[sintaks baru , yang sebelumnya tidak ada, mereka benar-benar dapat mengubah cara itu digunakan dengan cara yang kompatibel ke atas.

Ini mirip dengan evolusi yang menghasilkan $((...))sintaks untuk ekspresi aritmatika, yang sebagian besar menggantikan exprperintah tradisional .


0

Yang lebih baru [[di bashadalah optimasi dari [.

Klasik [memiliki satu kelemahan besar, ketika itu sering digunakan untuk melakukan operasi sepele: itu akan menelurkan proses baru setiap kali:
(Ini menciptakan ruang alamat baru hanya untuk membandingkan 0dan 1! Setiap kali!)

Saya pikir poin utama dari penambahan [[adalah membuat evaluasi ekspresi di dalam [tidak menelurkan proses tambahan. Tetapi bagaimana cara [kerjanya tidak bisa diubah - itu akan menciptakan banyak kebingungan dan masalah. Jadi, optimasi diimplementasikan dengan nama baru, dengan cara yang lebih efisien, yaitu perintah builtin shell.
Itu menjadi kata kunci dalam sintaksis shell sebagai efek samping.

Pada saat [pertama kali digunakan, itu adalah cara yang tepat untuk melakukannya dengan proses eksternal.


5
Perhatikan bahwa meskipun [awalnya merupakan perintah eksternal, itu ditambahkan sebagai built-in ke shell cukup awal, mungkin pada saat Unix System III dirilis dan pasti sebelum Unix System V dirilis. Jadi, 'proses ekstra' belum menjadi masalah sejak lama. Namun, sintaks tetap tidak berubah - itu diperlakukan seolah-olah itu akan menjadi perintah eksternal.
Jonathan Leffler

@JonathanLeffler Oh, terima kasih, saya merindukan [keduanya - itu berarti beberapa perubahan ...
Volker Siegel
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.