Mengapa kosong diperlukan antara “[[” dan “-e xxx” di ksh?

9

Misalnya, perintah berikut ini tidak berfungsi:

if [[-e xyz]]; then echo File exists;fi

ksh memberikan kesalahan berikut

[[-e: command not found

Apakah itu karena "[[-" ambigu?

AcBap
sumber
2
[[adalah kata kunci - mungkin pohon parsing shell mengharuskan kata kunci harus ditentukan oleh spasi
steeldriver
Cara lain untuk melihatnya adalah mungkin Anda bisa menulis fungsi [[-, bagaimana shell tahu untuk menguraikan "lakukan tanda e pada [[" alih-alih "lakukan fungsi [[- pada e".
pbhj

Jawaban:

15

Penjelasan yang paling sederhana adalah, karena dalam manual itu muncul [[ expression ]]sehingga harus ada ruang antara [[dan expressiondan penutup ]]. Tetapi tentu saja kita dapat mencoba untuk melihatnya secara lebih mendalam. Kerang memiliki tata bahasa yang agak rumit dan sangat bergantung pada konsep "pemisahan kata". Secara khusus, ksh (1) menyatakan manual:

Shell mulai mengurai inputnya dengan memecahnya menjadi kata-kata. Kata-kata, yang merupakan urutan karakter, dibatasi oleh karakter spasi putih yang tidak dikutip (spasi, tab, dan baris baru) atau meta-karakter (<,>, |,;, &, (,)). Selain membatasi kata, spasi dan tab diabaikan, sedangkan baris baru biasanya membatasi perintah.

Jadi seperti yang dinyatakan dalam manual, urutan karakter [[-edianggap kata shell. kshakan mencari perintah seperti itu dalam daftar built-in dan operator khusus (suka foratau while), kemudian mencari perintah eksternal - dan voilà - tidak ada yang ditemukan, maka ada pesan kesalahan tentang perintah tidak ditemukan.

Dalam hal ini [[juga bukan meta-karakter khusus. Jika ya, -ebagian dalam [[-eakan dianggap sebagai kata shell, bukan argumen untuk [[dirinya sendiri. Namun, [[dijelaskan sebagai perintah majemuk, dan manual mengatakan sebagai berikut:

Perintah majemuk dibuat menggunakan kata-kata yang dicadangkan berikut - kata-kata ini hanya dikenali jika mereka tidak dikutip dan jika digunakan sebagai kata pertama dari suatu perintah (yaitu, mereka tidak dapat didahului oleh penugasan parameter atau pengalihan):

Jadi agar [[dapat dikenali sebagai perintah majemuk, itu harus menjadi kata pertama dari perintah atau daftar perintah, yang menurut definisi sebelumnya dari "kata" menyiratkan itu harus dipisahkan oleh spasi, tab, atau baris baru dari lainnya kata-kata / argumen.


Anda telah bertanya di komentar : "Mengapa parser tidak bisa berhenti begitu menemukan" [["pola, dan memperlakukan itu sebagai awal dari perintah bersyarat?" Jawaban singkat mungkin karena 1) pengaruh [sintaksis karena awalnya merupakan perintah eksternal, dan untuk kepatuhan standar POSIX ada sebagai perintah eksternal bahkan hari ini, dan 2) karena parser shell dibangun demikian. Shell parser dapat mengenali karakter khusus yang tidak dibatasi ruang: echo $((2+2))dan (echo foobar)berfungsi dengan baik. Mungkin di masa depan ketika kshpengembangan dilanjutkan atau tampaknya ada percabangan atau klon (seperti mkshatau pdksh) seseorang akan mengimplementasikan sintaksis tanpa spasi di [[-e.

Lihat juga:

Sergiy Kolodyazhnyy
sumber
3
Saya pikir kutipan manual ksh benar-benar ada di tempat. Ini mirip dengan bahasa pemrograman lain: Di C, charadalah kata kunci tetapi Anda masih tidak dapat menulis charsomeletter = 'A';dan berharap parser berhenti setelah melihat char.
Peter - Pasang kembali Monica
Saya pikir perbedaan utama antara contoh Anda "char" dan simbol "[[" dalam pertanyaan adalah bahwa, pengidentifikasi alfanumerik, secara umum, membutuhkan pembatas ruang untuk memisahkan dari yang lain; simbol khusus sebagai token, di sisi lain, tidak perlu pembatas ruang.
AcBap
Persis. Saya tidak yakin perbandingan dengan C sangat membantu. Dalam C dapat dikatakan i--<=++j, dan kompiler tidak memiliki masalah menguraikan itu sebagai i -- <= ++ j.
Scott
@Scott Saya pikir perbandingan C sangat membantu (dan itu semacam sekunder bahwa pengidentifikasi C hanya memungkinkan alfanumerik dan _). Ksh memiliki (sebagai operator , dan (echo hello)mem - parsing sebagai ( echo hello ). Ini telah if, {, [[, dan lainnya kata kunci , dan ifx, {x, dan [[xmasing-masing satu token - seperti bagaimana charsomeletteradalah salah satu C tanda. Sebaliknya, tanda kutip (xdalam ksh adalah dua token - seperti bagaimana i--dua token dalam C. Apa yang secara konseptual berarti menjadi operator berbeda antara shell gaya Bourne (seperti ksh) dan C. Tapi Peter A. Schneider menunjukkan sebuah kesamaan leksikal nyata .
Eliah Kagan
2

Yang penting untuk dicatat adalah itu [adalah perintah, dan kata kunci shell [[dalam bash dan ksh didasarkan pada [.

[[digunakan dalam cara yang mirip dengan [. Kadang-kadang Anda bahkan dapat mengganti [ ]dengan [[ ]]tanpa perubahan perilaku. Dengan [, seperti perintah apa pun, spasi wajib antara perintah dan argumennya. Hal yang sama berlaku untuk [[.

Kami dulu hanya punya /usr/bin/[. Sekarang sebagian besar shell sudah [dibangun untuk efisiensi — tetapi sintaksnya sama. Dalam shell yang menyediakan [[, itu berfungsi sebagai alternatif yang lebih fleksibel[ .

Berikut ini deskripsi untuk [di bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Jadi [sama dengan test(selain mengharapkan ]argumen di akhir). help testakan memberikan detail lebih banyak tentang itu. Anda dapat membandingkan ini dengan help [[.

Ada juga halaman manual untuk perintah eksternal [( man \[).

Dalam kasus

if [[-e
  • Baik [juga [[adalah perintah, ada. Kata lengkapnya [[-eadalah.
  • Itu if [[-emembuatnya menjadi ujian untuk benar / salah. Jadi, apakah [[-eada perintah ?

Apakah itu karena "[[-" ambigu?

Iya. Atau tidak. [[-eadalah apa adanya: tidak ada yang dipahami shell sehingga ia menganggap itu adalah perintahnya sendiri. ;-)

Rinzwind
sumber
"% help [[" "mengatakan bahwa" [["adalah perintah kondisional. Mengapa parser tidak bisa berhenti begitu menemukan "[" "pola, dan memperlakukan itu sebagai awal dari perintah bersyarat?
AcBap
@Myloti [[-eadalah nama perintah yang berpotensi valid (jika terlihat aneh).
Gordon Davisson
2

Ruang adalah pembatas dan diperlukan. Seperti yang Anda lihat dari shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Baik ksh dan bash mendukung [[dan tidak bekerja tanpa spasi. Shellcheck memberikan output yang persis sama dengan skrip ksh dan bash yang berisi baris buggy.)

Mengapa pembatas diperlukan karena token dan leksikon .

WinEunuuchs2Unix
sumber
Harap dicatat, OP bertanya tentang kshshell dalam pertanyaan. Bash meminjam [[operator dari kshdan dalam pertanyaan ini perilaku mereka identik, tapi saya akan berhati-hati dari asumsi karena ada beberapa perbedaan yang signifikan antara kshdan bashperilaku dan internal. Itu hanya karena shellcheck berfungsi pada skrip bash, mungkin itu bukan alat yang tepat untuk skrip ksh. Hanya sesuatu yang perlu diingat ketika mendekati kerang yang berbeda
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Saya menjadi oportunistik menggunakan shellcheck untuk menyoroti [[aturan bawaan . Saya sama sekali tidak dapat menyimpulkan bahwa kshitu adalah shell yang shellcheckdapat menemukan kesalahan. Saya berharap orang lain menghargai kshdan bashdua penafsir yang berbeda. Terima kasih telah menyebutkannya. Juga patut dicatat [[adalah bash yang dibangun sementara [adalah perintah eksternal.
WinEunuuchs2Unix
Sebenarnya [juga built-in di bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Tapi Anda tidak jauh - [atau testdiharuskan menjadi perintah eksternal. Pada masa shell Bourne asli [sebenarnya merupakan perintah eksternal, dan masih ada saat ini /usr/bin/[karena POSIX mengharuskannya menjadi perintah eksternal pubs.opengroup.org/onlinepubs/009695399/utilities/test.html Sangat sedikit yang diperlukan oleh POSIX untuk menjadi built-in pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Builtin [untuk efisiensi
Sergiy Kolodyazhnyy
2
Shellcheck tahu ksh; menggunakan hashbang atau -s ksh. Btw, ksh dan bash memiliki [["built in," tapi itu kata kunci , tidak seperti [, yang merupakan builtin shell . (ksh :; whence -v [ [[bash type [ [[:) Builtin dan perintah eksternal secara sintaksis sama. Salah satu cara [[berbeda dari itu [adalah yang [[menekan beberapa ekspansi dalam argumennya, yang tidak bisa sebagai builtin - sama seperti {tidak dapat melakukan pengelompokan jika itu builtin. Ini mungkin mengapa {x(atau [[x) menjadi satu token terasa aneh. Saya pikir itu benar untuk mengatakan builtin dan kata kunci secara leksikal tetapi tidak mirip secara sintaksis . @SergiyKolodyazhnyy
Eliah Kagan
1
@EliahKagan Sebenarnya, saya telah memposting pertanyaan pada U&L untuk mendapatkan jawaban yang tepat tentang apa yang POSIX pikirkan tentang situasi ini unix.stackexchange.com/questions/526574/… Bahasa shell cukup rumit sehingga mudah-mudahan seseorang dapat menjelaskannya dalam istilah yang lebih formal
Sergiy Kolodyazhnyy
1

[[bisa menjadi perintah eksternal! Artinya, ini mungkin program dan bukan sintaks yang didukung oleh shell Anda secara langsung. Mendukung sintaksis tanpa spasi mungkin untuk kshtetapi akan gagal pada sistem dengan eksternal [[sehingga untuk alasan kompatibilitas lebih baik untuk menjaga ruang yang diperlukan.

BusyBox menyediakan [[sebagai perintah eksternal , misalnya.

DarkDust
sumber
0

Karena [[-edapat berpartisipasi dalam ekspansi shell (dapat digunakan seperti

echo [[-e]*

untuk membuat daftar semua file yang dimulai dengan huruf di antara [dan secara einklusif), itu akan menjadi kekacauan total jika [dan ]merupakan karakter khusus yang tidak berpartisipasi dalam pemisahan kata yang diatur oleh spasi.


sumber