Bagaimana cara kerja shebang ini yang dimulai dengan tanda hubung ganda (-)?


14

Saya telah menemukan jenis shebang berikut di halaman RosettaCode:

--() { :; }; exec db2 -txf "$0"

Ini berfungsi untuk Db2, dan hal serupa untuk Postgres. Namun, saya tidak mengerti seluruh baris.

Saya tahu tanda hubung ganda adalah komentar dalam SQL, dan setelah itu ia memanggil Db2 yang dapat dieksekusi dengan beberapa parameter yang meneruskan file itu sendiri sebagai file. Tapi bagaimana dengan kurung, kurung keriting, usus besar dan semi-kolon, dan bagaimana bisa menggantikan # shebang asli! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

Jawaban:


18

Terkait: Interpreter shell mana yang menjalankan skrip tanpa shebang?

Script tidak memiliki shebang / hashbang / #!line, hanya karena dash ganda tidak #!.

Namun, skrip akan dieksekusi oleh shell (lihat pertanyaan dan jawaban terkait di atas), dan di shell itu, jika -karakter yang valid dalam nama fungsi, baris menyatakan fungsi shell yang disebut --tidak melakukan apa-apa (yah, itu berjalan :, yang tidak melakukan apa-apa ) dan yang tidak pernah disebut.

Fungsinya, dalam notasi multi-garis yang lebih umum (hanya untuk membuatnya lebih jelas seperti apa, karena namanya yang aneh agak mengaburkan fakta bahwa itu sebenarnya sebuah fungsi):

-- () {
  :
}

Satu-satunya tujuan dari definisi fungsi adalah untuk memiliki garis yang valid dalam skrip shell dan pada saat yang sama perintah SQL yang valid (komentar). Kode semacam ini disebut polyglot .

Setelah mendeklarasikan fungsi shell palsu, skrip, ketika dijalankan oleh penerjemah skrip shell, digunakan execuntuk mengganti shell saat ini dengan proses yang dihasilkan dari menjalankan db2 -txf "$0", yang akan sama dengan menggunakan db2 -txfpathname dari skrip dari baris perintah.

Trik ini mungkin tidak akan berfungsi dengan baik pada sistem di mana dashatau ashshell berbasis lainnya yash,, Bourne shell, ksh88atau ksh93digunakan sebagai /bin/sh, karena shell ini tidak menerima fungsi yang namanya berisi tanda hubung.

Juga terkait:


Saya kira yang berikut ini juga akan berfungsi (tidak benar-benar diuji):

--() { exec db2 -txf "$0"; }; --

@ilkkachu Lebih baik sekarang?
Kusalananda

1
Oh ya! Dan terima kasih sudah mengingatkan saya akan hal apa itu namanya. :)
ilkkachu

6

Seperti @Kusalananda telah katakan, trik itu rusak dan tidak akan bekerja di semua shell.

Inilah pendapat saya tentang melakukannya dengan mudah:

--/.. 2>/dev/null; exec db2 -txf "$0"

Perintah pertama harus gagal meskipun file / direktori bernama --ada di direktori saat ini dan kesalahan akan ditutup oleh 2>/dev/null; shell kemudian akan melanjutkan dengan perintah kedua, perintah exec.


Ini masih belum benar-benar portabel. Ini bukan skrip yang valid dan Anda masih mengandalkan shell panggilan untuk mengatasi kenyataan bahwa kernel akan menolak untuk menjalankan skrip dan kembali ENOEXECjika Anda mencobanya. Coba jalankan skrip di bawah straceuntuk melihat apa yang saya maksud.
kasperd

@kasperd, itu harus tetap portabel, shell seharusnya menjalankan skrip sebagai skrip shell jika exec()tidak bekerja di atasnya. "Jika fungsi execl () gagal karena kesalahan yang setara dengan kesalahan [ENOEXEC], shell akan menjalankan perintah yang setara dengan membuat shell dipanggil dengan nama perintah sebagai operan pertamanya, ..." (lihat pubs.opengroup .org / onlinepubs / 9699919799.2018edition / utilities /… )
ilkkachu

@ilkkachu Tapi skrip tidak selalu dijalankan dari shell. Jika Anda mencoba menggunakan skrip dalam konteks lain di mana executable akan berfungsi, itu akan gagal. Lagipula shells tidak menyetujui juru bahasa mana yang digunakan. Jadi skrip Anda sekarang akan berperilaku berbeda atau gagal sama sekali tergantung pada konteks dari mana ia dipanggil.
kasperd

@kasperd, well, tentu, itu tidak akan berfungsi jika Anda exec()langsung dari sesuatu selain shell. Tapi apa yang akan terjadi? Anda mungkin ingin menjalankan skrip dari cronatau semacamnya, tapi saya pikir itu menjalankan semuanya melalui shell, dan bahkan jika tidak, mudah untuk mengeja db2 -txf /path/to/scriptdalam kasus itu, karena Anda hanya perlu melakukannya sekali. Memiliki pekerjaan steno sebagian besar berguna pada shell interaktif. Tapi tentu saja, skrip pembungkus yang terpisah mungkin lebih kuat.
ilkkachu

1
@kasperd Saya tidak akan mengganggu Anda dengan dokumen dan standar; Cobalah! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Paman Billy
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.