Mengapa spasi kadang-kadang dibutuhkan di sekitar karakter meta?

544

Beberapa bulan yang lalu saya menato bom garpu di lengan saya, dan saya melewatkan ruang putih, karena saya pikir itu terlihat lebih baik tanpa mereka. Tetapi saya kaget, kadang-kadang (tidak selalu) ketika saya menjalankannya di shell, ia tidak memulai bom fork, tetapi itu hanya memberikan kesalahan sintaksis.

bash: syntax error near unexpected token `{:'

Kemarin itu terjadi ketika saya mencoba menjalankannya di shell Bash teman , dan kemudian saya menambahkan spasi putih dan tiba-tiba bekerja, :(){ :|:& };:bukannya:(){:|:&};:

Apakah masalah spasi putih; sudahkah saya menato kesalahan sintaksis di lengan saya ?!

Tampaknya selalu bekerja di zsh , tetapi tidak di Bash.

Pertanyaan terkait tidak menjelaskan apa pun tentang ruang putih, yang sebenarnya adalah pertanyaan saya; Mengapa ruang putih diperlukan untuk Bash agar dapat menguraikannya dengan benar?

spydon
sumber
6
Saya memposting pertanyaan yang sama di sini (tidak termasuk bagian tato).
Benoit
3
Juga, titik dua (:) tidak dapat digunakan sebagai nama fungsi (lihat: pubs.opengroup.org/onlinepubs/9699919799/utilities/ ...) ... FreeBSD / bin / sh bahkan memberikan kesalahan pada ...
Martin Tournoij
5
@Carpetsmoker: Saya tidak yakin bagaimana itu relevan. Pertanyaan ini tentang Bash.
Dennis

Jawaban:

268

Ada daftar karakter yang memisahkan token di BASH. Karakter ini disebut metakarakter dan mereka |, &, ;, (, ), <, >, ruang dan tab . Di sisi lain, kurung kurawal ( {dan }) hanyalah karakter biasa yang membentuk kata-kata.

Menghilangkan ruang kedua sebelumnya }akan dilakukan, karena &merupakan metacharacter. Karena itu, tato Anda harus memiliki setidaknya satu karakter spasi.

:(){ :|:&};:
Dmitri Chubarov
sumber
35
Solusi mudah: pindahkan bagian pertama taoo ke kiri dan transplantasi kulit di antara dua bagian.
23
Saya menyukai istilah, on the other hand... pun intended? ;): D (Maaf, karena komentar topik).
anishsane
4
Tapi ini berbeda untuk zsh? Dalam hal apa zsh berbeda?
tfogo
2
Penjelasan yang baik, kecuali bahwa {dan }adalah kata kunci shell dalam konteks ini. Hanya jika mereka tidak dikenali seperti itu - karena kurangnya ruang / metakarakter di sekitarnya - mereka diperlakukan sebagai bagian harfiah dari kata mereka menjadi bagian dari. (Dan kemudian ada ekspansi penjepit, yang terjadi dalam konteks yang berbeda.)
mklement0
2
@DmitriChubarov Braces diizinkan dalam nama perintah (termasuk fungsi:. {foo} () { echo hello; }"Nama", sebagaimana didefinisikan oleh bashsebagai "kata yang hanya terdiri dari karakter alfanumerik dan garis bawah, dan dimulai dengan karakter alfabet atau garis bawah", hanya berlaku untuk nama variabel.
chepner
82

Hanya tato a

#!/bin/zsh

Shebang di atasnya dan Anda akan baik-baik saja.

SzG
sumber
6
Jika kita pilih-pilih, shebang tidak akan berfungsi sama sekali, karena shell, semoga zsh, berada dalam mode interaktif, sebagaimana dibuktikan oleh prompt ...
SzG
50

Kawat gigi lebih seperti kata kunci aneh daripada simbol khusus, dan memang perlu spasi. Ini berbeda dengan tanda kurung, misalnya. Membandingkan:

(ls)

yang bekerja, dan:

{ls}

yang mencari perintah bernama {ls}. Untuk bekerja, itu harus:

{ ls; }

Tanda titik koma menghentikan kurung kurawal yang diambil sebagai parameter ls.

Yang harus Anda lakukan adalah memberi tahu orang-orang bahwa Anda menggunakan font proporsional dengan karakter spasi yang agak sempit.

Peter Westlake
sumber
12
@DmitriChubarov - ini adalah trik yang sangat licik, menggunakan makna kawat gigi yang sangat berbeda. Ini memperluas daftar nilai yang dipisahkan koma, yang dalam hal ini hanyalah ls.
Peter Westlake
7
Tapi ... nak ... kamu akan memiliki tato untuk sisa hidupmu! Anda menandai diri Anda sebagai kutu buku untuk selamanya! Dan kemudian Anda bahkan tidak bisa melakukannya dengan benar sebelum dijahit? Itu dua langkah di luar menjadi kutu buku kurasa ;-) Jangan tersinggung, kawan, aku hanya ingin tahu.
Alfe
14
@ Alfe Saya mencobanya sebelum saya menato, tapi saya mencobanya di zsh saya, saya pikir itu memiliki parsing yang sama dengan bash. Bodoh dari saya, tapi saya hanya akan memberitahu orang-orang bahwa itu zsh. :)
spydon
20
@spydon Atau Anda memberi tahu mereka bahwa Anda sengaja meninggalkan ruang itu sehingga orang yang menyalin perintah dari tato Anda tidak akan sengaja menjalankannya dan menabrak mesin mereka;)
colek
4
Hai, jika ini berlaku di zsh, mengapa tidak menambahkan #! / Bin / zsh tepat di atasnya (atau sebelum itu)? Sebaiknya Anda menentukan shell terlebih dahulu.
Adam Miller
41

Meskipun tidak mudah terlihat di font tatoo, sebenarnya ada Byte-Order Mark (BOM) antara penjepit dan titik dua (Anda mungkin sudah cukup mabuk ketika Anda mendapat tatoo yang tidak Anda perhatikan, tetapi itu benar-benar ada) . Ini menyisakan tiga kemungkinan yang jelas:

  1. Anda gagal mengetikkan BOM ketika Anda menyalin kode. Hasilnya adalah aplikasi yang jelas dari GIGO. Shell tidak mengenali BOM yang tidak ada dalam transkripsi Anda yang gagal.
  2. Shell Anda terlalu tua. Itu tidak mengenali karakter Unicode, sehingga BOM (dan mungkin semua karakter Unicode lainnya) diabaikan sepenuhnya, meskipun BOM di mana saja tetapi awal file seharusnya dianggap sebagai lebar nol, ruang tanpa-putus .
  3. Shell Anda terlalu baru. Penggunaan BOM sebagai ZWNBS sudah usang, dan penulis telah mengimplementasikan versi Unicode di masa depan di mana penggunaan ini tidak lagi diizinkan.
Jerry Coffin
sumber
40

dan kemudian saya menambahkan spasi putih dan tiba-tiba berhasil ...

Itu karena bagaimana shell mengurai. Anda memerlukan spasi setelah definisi fungsi dimulai, yaitu setelah {.

foo() { echo hey& }
foo() { echo hey&}
foo(){ echo hey&}

valid Di samping itu,

foo() {echo hey&}

bukan.


Anda sebenarnya membutuhkan tato seperti ini:

masukkan deskripsi gambar di sini


Dari sumber :

  /* We ignore an open brace surrounded by whitespace, and also
     an open brace followed immediately by a close brace preceded
     by whitespace.  */

Menghilangkan spasi setelah {menyebabkan {echoditafsirkan sebagai token tunggal.


Bentuk setara dengan

:(){ :|:& };:

akan menjadi

:(){
:|:& };:

Perhatikan bahwa tidak ada ruang setelah {dalam versi alternatif, tetapi satu baris menyebabkan shell untuk mengenali {sebagai token.

devnull
sumber
"Menghilangkan spasi setelah {menyebabkan {echo ditafsirkan sebagai token tunggal." - jadi apakah parser percaya telah menemukan perintah yang dipanggil {echosebelum mencapai brace pembuka yang diperlukan?
Jonah