Mengapa perilaku sintaks `#!` Tidak ditentukan oleh POSIX?

17

Dari halaman Bahasa Perintah Shell dari spesifikasi POSIX:

Jika baris pertama file perintah shell dimulai dengan karakter "#!", Hasilnya tidak ditentukan.

Mengapa perilaku yang #!tidak ditentukan oleh POSIX? Saya merasa bingung bahwa sesuatu yang begitu portabel dan banyak digunakan akan memiliki perilaku yang tidak ditentukan.

Harold Fischer
sumber
1
Standar meninggalkan hal-hal yang tidak ditentukan untuk tidak mengikat implementasi ke perilaku tertentu. Misalnya, "login" adalah "Aktivitas yang tidak ditentukan di mana pengguna mendapatkan akses ke sistem."
Kusalananda
2
Karena POSIX tidak menentukan jalur yang dapat dieksekusi, garis shebang pada dasarnya tidak-portabel; Saya tidak yakin banyak yang akan diperoleh dengan menspesifikasikannya.
Michael Homer
1
@MichaelHomer, tentu saja tidak? Standar dapat menentukan bahwa garis tersebut berisi jalur yang digunakan untuk penerjemah, bahkan tanpa memberi tahu jalan apa yang seharusnya.
ilkkachu
1
@ HaroldFischer Kecuali itu tidak ditafsirkan oleh shell, itu ditafsirkan oleh kernel OS (dilakukan setidaknya di Linux, yang sebenarnya dapat menonaktifkan dukungan ini selama waktu pembangunan), atau perpustakaan apa pun yang mengimplementasikan exec()fungsi. Jadi memeriksa beberapa shell tidak benar-benar memberitahu Anda seberapa portabelnya.
Austin Hemmelgarn
2
@ HaroldFischer Selain itu, bahkan di antara OS yang mendukung POSIX, perilaku ini tidak konsisten. Linux dan macOS berperilaku berbeda: Linux tidak sepenuhnya tokenize garis shebang oleh spasi. macOS tidak mengizinkan penerjemah skrip menjadi skrip lain. Juga lihat en.wikipedia.org/wiki/Shebang_(Unix)#Portabilitas
jamesdlin

Jawaban:

21

Saya pikir terutama karena:

  • perilaku sangat bervariasi antara implementasi. Lihat https://www.in-ulm.de/~mascheck/various/shebang/ untuk semua detailnya.

    Namun sekarang bisa menentukan subset minimum dari sebagian besar implementasi mirip Unix: seperti #! *[^ ]+( +[^ ]+)?\n(dengan hanya karakter dari karakter nama file portabel yang diatur dalam satu atau dua kata) di mana kata pertama adalah jalur absolut ke executable asli, masalahnya tidak terlalu lama dan perilaku tidak ditentukan jika executable adalah setuid / setgid, dan implementasinya ditentukan apakah jalur juru bahasa atau jalur skrip diteruskan argv[0]ke juru bahasa.

  • POSIX tidak menentukan jalur yang dapat dieksekusi. Beberapa sistem memiliki utilitas pra-POSIX di /bin/ /usr/bindan memiliki utilitas POSIX di tempat lain (seperti pada Solaris 10 di mana /bin/shterdapat Bourne shell dan yang POSIX ada di /usr/xpg4/bindalamnya; Solaris 11 menggantinya dengan ksh93 yang lebih sesuai dengan POSIX, tetapi sebagian besar yang lain alat di /binmasih yang non-POSIX kuno). Beberapa sistem bukan yang POSIX tetapi memiliki mode / emulasi POSIX. Semua POSIX memerlukan adalah bahwa ada lingkungan yang terdokumentasi di mana sistem berperilaku POSIXly.

    Lihat Windows + Cygwin misalnya. Sebenarnya, dengan Windows + Cygwin, she-bang dihormati ketika skrip dipanggil oleh aplikasi cygwin, tetapi tidak oleh aplikasi Windows asli.

    Jadi, bahkan jika POSIX menentukan mekanisme shebang, ia tidak dapat digunakan untuk menulis skrip POSIX sh/ sed/ awk... (juga perhatikan bahwa mekanisme shebang tidak dapat digunakan untuk menulis skrip sed/ dapat diandalkan awkkarena tidak mengizinkan melewati opsi akhir opsi penanda).

Sekarang fakta bahwa itu tidak ditentukan tidak berarti Anda tidak dapat menggunakannya (well, ia mengatakan Anda tidak harus memulai dengan baris pertama #!jika Anda mengharapkannya hanya komentar biasa dan bukan omong kosong), tetapi bahwa POSIX tidak memberi Anda jaminan jika Anda melakukannya.

Dalam pengalaman saya, menggunakan shebang memberi Anda lebih banyak jaminan portabilitas daripada menggunakan cara POSIX untuk menulis skrip shell: tinggalkan she-bang, tulis skrip dalam shsintaks POSIX dan berharap bahwa apa pun yang memanggil skrip memanggil POSIX compliant shdi atasnya, yang merupakan boleh jika Anda tahu skrip akan dipanggil di lingkungan yang tepat oleh alat yang tepat tetapi tidak sebaliknya.

Anda mungkin harus melakukan hal-hal seperti:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Jika Anda ingin portabel untuk Windows + Cygwin, Anda mungkin harus memberi nama file Anda dengan .batatau .ps1ekstensi dan menggunakan beberapa trik serupa untuk cmd.exeatau powershell.exememanggil cygwin shpada file yang sama.

Stéphane Chazelas
sumber
Menariknya, mulai edisi 5 : "Konstruk #! Dicadangkan untuk implementasi yang ingin memberikan ekstensi itu. Aplikasi portabel tidak dapat menggunakan #! Sebagai baris pertama skrip shell; mungkin tidak diartikan sebagai komentar."
muru
@muru Jika skripnya benar-benar portabel, pada sistem POSIX yang menjalankan POSIX sh, skrip tersebut tidak memerlukan baris hashbang karena akan dijalankan oleh POSIX sh.
Kusalananda
1
@ Kusalananda itu hanya benar jika execlpatau execvpdigunakan, kan? Jika saya menggunakan execve, itu akan menghasilkan ENOEXEC?
muru
9

Perilaku itu tampaknya konsisten di antara semua kerang POSIX. Saya tidak melihat perlunya ruang gerak di sini.

Anda tidak melihat cukup dalam.

Kembali pada 1980-an, mekanisme ini tidak standar de facto. Meskipun Dennis Ritchie telah mengimplementasikannya, implementasi itu belum mencapai publik di sisi AT&T alam semesta. Secara efektif hanya tersedia untuk umum dan dikenal di BSD; dengan skrip shell yang dapat dieksekusi tidak tersedia di AT&T Unix. Jadi tidak masuk akal untuk membakukannya. Keadaan hubungan dicontohkan oleh dokumen kontemporer ini, salah satu dari banyak seperti:

Perhatikan bahwa BSD memungkinkan file yang dimulai dengan #! interpreterdieksekusi secara langsung, sedangkan SysV hanya mengizinkan file a.out untuk dieksekusi secara langsung. Ini berarti bahwa instance dari salah satu exec…()rutin dalam program BSD mungkin harus diubah di bawah SysV untuk menjalankan penerjemah (secara typlic /bin/sh) untuk program itu.
- Stephen Frede (1988). "Pemrograman pada Sistem X Rilis Y". Newsletter Kelompok Pengguna Australian Unix Systems . Volume 9. Nomor 4. p. 111.

Poin penting di sini adalah bahwa Anda melihat shell, sedangkan keberadaan skrip shell yang dapat dieksekusi sebenarnya adalah masalah exec…()fungsi. Apa yang dilakukan oleh shell termasuk prekursor dari mekanisme skrip yang dapat dieksekusi, masih dapat ditemukan di beberapa shell bahkan hari ini (dan juga saat ini diamanatkan untuk exec…p()subset fungsi), dan agak menyesatkan. Apa yang perlu diperhatikan oleh standar dalam hal ini adalah bagaimana exec…()pada skrip yang diinterpretasikan bekerja, dan pada saat POSIX awalnya dibuat, ia sama sekali tidak bekerja di tempat pertama di seluruh bagian utama dari spektrum sistem operasi target .

Pertanyaan bawahan adalah mengapa hal ini belum distandarisasi sejak saat itu, terutama karena mekanisme angka ajaib untuk penerjemah skrip telah mencapai publik di sisi AT&T alam semesta dan telah didokumentasikan exec…()dalam System 5 Interface Definition , pada pergantian tahun 1990-an. :

File juru bahasa dimulai dengan satu baris formulir

#! pathname [arg]
di mana pathname adalah path dari interpreter, dan arg adalah argumen opsional. Ketika Anda execfile juru bahasa, sistem execadalah juru bahasa yang ditentukan.
- exec. Sistem V Interface Definition . Volume 1. 1991.

Sayangnya, tingkah laku ini masih berbeda hingga hampir sama dengan tahun 1980-an dan tidak ada perilaku yang benar-benar umum untuk dibakukan. Beberapa Unices (misalnya HP-UX dan FreeBSD, misalnya) tidak mendukung skrip sebagai penerjemah skrip. Apakah baris pertama adalah satu, dua, atau banyak elemen yang dipisahkan oleh spasi putih bervariasi antara MacOS (dan versi FreeBSD sebelum 2005) dan lainnya. Panjang jalur maksimum yang didukung bervariasi. dan karakter melebihi set karakter nama file portabel POSIX yang rumit, seperti halnya memimpin dan mengikuti spasi putih. Apa argumen 0, 1, dan 2 akhirnya juga rumit, dengan variasi yang signifikan di seluruh sistem. Beberapa saat ini POSIX-conformant tetapi nonSistem -Unix masih tidak mendukung mekanisme seperti itu, dan mengamanatkan itu akan mengubahnya menjadi tidak lagi sesuai POSIX.

Bacaan lebih lanjut

JdeBP
sumber
1

Sebagaimana dicatat oleh beberapa jawaban lain, implementasi bervariasi. Ini membuat sulit untuk menstandarisasi dan mempertahankan kompatibilitas ke belakang dengan skrip yang ada. Ini berlaku bahkan untuk sistem POSIX modern. Sebagai contoh, Linux tidak sepenuhnya tokenize garis shebang oleh spasi. macOS tidak mengizinkan penerjemah skrip menjadi skrip lain.

Lihat juga http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
sumber