Saya bertanya-tanya apakah ada cara umum untuk melewatkan beberapa opsi ke file yang dapat dieksekusi melalui baris shebang ( #!
).
Saya menggunakan NixOS, dan bagian pertama dari shebang dalam skrip apa pun yang saya tulis biasanya /usr/bin/env
. Masalah yang saya temui kemudian adalah bahwa semua yang muncul setelah ditafsirkan sebagai satu file atau direktori oleh sistem.
Misalkan, misalnya, saya ingin menulis skrip yang akan dieksekusi bash
dalam mode posix. Cara naif menulis shebang adalah:
#!/usr/bin/env bash --posix
tetapi mencoba menjalankan skrip yang dihasilkan menghasilkan kesalahan berikut:
/usr/bin/env: ‘bash --posix’: No such file or directory
Saya mengetahui posting ini , tetapi saya bertanya-tanya apakah ada solusi yang lebih umum dan lebih bersih.
EDIT : Saya tahu bahwa untuk skrip Guile , ada cara untuk mencapai apa yang saya inginkan, didokumentasikan dalam Bagian 4.3.4 manual:
#!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#
Kuncinya, di sini, adalah bahwa baris kedua (dimulai dengan exec
) ditafsirkan sebagai kode oleh sh
tetapi, berada di #!
... !#
blok, sebagai komentar, dan dengan demikian diabaikan, oleh juru bahasa Guile.
Apakah tidak mungkin untuk menggeneralisasi metode ini kepada penerjemah mana pun?
EDIT Kedua : Setelah bermain-main sedikit, tampaknya, untuk penerjemah yang dapat membaca masukan dari mereka stdin
, metode berikut akan bekerja:
#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;
Ini mungkin tidak optimal, meskipun, karena sh
prosesnya hidup sampai penerjemah menyelesaikan tugasnya. Umpan balik atau saran apa pun akan dihargai.
Jawaban:
Tidak ada solusi umum, setidaknya tidak jika Anda perlu mendukung Linux, karena kernel Linux memperlakukan semuanya mengikuti "kata" pertama di baris shebang sebagai argumen tunggal .
Saya tidak yakin apa kendala NixOS, tetapi biasanya saya hanya akan menuliskan shebang Anda
atau, jika memungkinkan, mengatur opsi dalam skrip :
Atau, Anda dapat meminta script memulai ulang sendiri dengan permintaan shell yang sesuai:
Pendekatan ini dapat digeneralisasi ke bahasa lain, selama Anda menemukan cara untuk beberapa baris pertama (yang ditafsirkan oleh shell) untuk diabaikan oleh bahasa target.
GNU
coreutils
'env
menyediakan solusi sejak versi 8.30, lihat unode ‘s jawaban untuk rincian. (Ini tersedia dalam Debian 10 dan yang lebih baru, RHEL 8 dan yang lebih baru, Ubuntu 19.04 dan yang lebih baru, dll.)sumber
Meskipun tidak sepenuhnya portabel, dimulai dengan coreutils 8.30 dan menurut dokumentasinya Anda dapat menggunakan:
Jadi diberikan:
kamu akan mendapatkan:
dan jika Anda penasaran
showargs
adalah:sumber
env
mana-S
ditambahkan pada tahun 2005. See lists.gnu.org/r/coreutils/2018-04/msg00011.htmlshowargs
: pastebin.com/q9m6xr8H dan pastebin.com/gS8AQ5WA (satu-liner)env
termasuk miliknya sendirishowargs
: opsi -v misalnya#!/usr/bin/env -vS --option1 --option2 ...
Standar POSIX sangat singkat dalam menggambarkan
#!
:Dari bagian pemikiran dokumentasi
exec()
keluarga antarmuka sistem :Dari bagian Pengantar Shell :
Ini pada dasarnya berarti bahwa implementasi apa pun (Unix yang Anda gunakan) bebas untuk melakukan spesifikasi parsing dari garis shebang seperti yang diinginkan.
Beberapa Unices, seperti macOS (tidak dapat menguji ATM), akan membagi argumen yang diberikan kepada penerjemah pada baris shebang menjadi argumen yang terpisah, sementara Linux dan sebagian besar Unices lainnya akan memberikan argumen sebagai opsi tunggal untuk penerjemah.
Karena itu tidak bijaksana untuk mengandalkan garis shebang untuk dapat mengambil lebih dari satu argumen.
Lihat juga bagian Portabilitas artikel Shebang di Wikipedia .
Salah satu solusi mudah, yang dapat digeneralisasikan untuk utilitas atau bahasa apa pun, adalah membuat skrip wrapper yang mengeksekusi skrip asli dengan argumen baris perintah yang sesuai:
Saya tidak berpikir saya akan secara pribadi mencoba membuatnya kembali mengeksekusi sendiri karena rasanya agak rapuh.
sumber
Shebang dijelaskan dalam
execve
(2) halaman manual sebagai berikut:Dua spasi diterima dalam sintaks ini:
Perhatikan bahwa saya tidak menggunakan jamak ketika berbicara tentang argumen opsional, sintaksis di atas juga tidak digunakan
[optional-arg ...]
, karena Anda dapat memberikan paling banyak satu argumen tunggal .Sejauh menyangkut skrip shell, Anda dapat menggunakan
set
perintah bawaan di dekat bagian awal skrip Anda yang akan memungkinkan untuk mengatur parameter penerjemah, memberikan hasil yang sama seperti jika Anda menggunakan argumen baris perintah.Dalam kasus Anda:
Dari prompt Bash, periksa output
help set
untuk mendapatkan semua opsi yang tersedia.sumber
Di Linux, shebang tidak terlalu fleksibel; menurut beberapa jawaban (jawaban Stephen Kitt dan Jörg W Mittag ), tidak ada cara yang ditentukan untuk melewati beberapa argumen dalam garis shebang.
Saya tidak yakin apakah ini akan berguna bagi siapa pun, tetapi saya telah menulis skrip pendek untuk mengimplementasikan fitur yang kurang. Lihat https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa .
Dimungkinkan juga untuk menulis solusi yang disematkan. Di bawah, saya sajikan empat solusi bahasa-agnostik yang diterapkan pada skrip uji yang sama dan hasil masing-masing dicetak. Saya kira bahwa script adalah dieksekusi dan di
/tmp/shebang
.Membungkus skrip Anda di bash heredoc di dalam subtitusi proses
Sejauh yang saya tahu, ini adalah cara agnostik bahasa yang paling dapat diandalkan untuk melakukannya. Ini memungkinkan lewat argumen dan mempertahankan stdin. Kekurangannya adalah bahwa penerjemah tidak mengetahui lokasi (sebenarnya) dari file yang dibacanya.
Memanggil
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
cetakan:Perhatikan bahwa substitusi proses menghasilkan file khusus. Ini mungkin tidak sesuai dengan semua yang dapat dieksekusi. Misalnya,
#!/usr/bin/less
mengeluh:/dev/fd/63 is not a regular file (use -f to see it)
Saya tidak tahu apakah mungkin untuk memiliki heredoc di dalam proses substitusi di dash.
Membungkus naskah Anda dalam heredoc sederhana
Lebih pendek dan sederhana, tetapi Anda tidak akan dapat mengakses
stdin
dari skrip Anda dan itu membutuhkan penerjemah untuk dapat membaca dan menjalankan skrip daristdin
.Memanggil
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
cetakan:Gunakan
system()
panggilan awk tetapi tanpa argumenBenar melewati nama file yang dieksekusi, tetapi skrip Anda tidak akan menerima argumen yang Anda berikan. Perhatikan bahwa awk adalah satu-satunya bahasa yang saya tahu yang interpreter keduanya diinstal di linux secara default dan membaca instruksinya dari baris perintah secara default.
Memanggil
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
cetakan:Gunakan
system()
panggilan 4.1+ awk , asalkan argumen Anda tidak mengandung spasiBagus, tetapi hanya jika Anda yakin skrip Anda tidak akan dipanggil dengan argumen yang berisi spasi. Seperti yang dapat Anda lihat, argumen Anda yang berisi spasi akan dipisah, kecuali spasi tersebut lolos.
Memanggil
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
cetakan:Untuk versi awk di bawah 4.1, Anda harus menggunakan penggabungan string di dalam for for, lihat contoh fungsi https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .
sumber
$variable
atau`command`
mengganti:exec python3 -O <(cat <<'EOWRAPPER'
Trik untuk digunakan
LD_LIBRARY_PATH
dengan python pada baris#!
(shebang) yang tidak bergantung pada apa pun selain shell dan menjalankan suguhan:Seperti yang dijelaskan di bagian lain halaman ini, beberapa shell seperti
sh
dapat menggunakan script pada input standar mereka.Skrip yang kami berikan
sh
mencoba menjalankan perintah''''
yang disederhanakan''
(string kosong) olehsh
dan tentu saja gagal menjalankannya karena tidak ada''
perintah, sehingga biasanya ditampilkanline 2: command not found
pada deskriptor kesalahan standar tetapi kami mengarahkan pesan ini menggunakan2>/dev/null
ke lubang hitam terdekat karena itu akan berantakan dan membingungkan bagi pengguna untuk membiarkannyash
ditampilkan.Kami kemudian melanjutkan ke perintah yang menarik bagi kami:
exec
yang menggantikan proses shell saat ini dengan yang berikut, dalam kasus kami:/usr/bin/env python
dengan parameter yang memadai:"$0"
untuk memberi tahu python skrip mana yang harus dibuka dan ditafsirkan, dan juga disetelsys.argv[0]
"$@"
untuk mengatur pythonsys.argv[1:]
ke argumen yang diteruskan pada baris perintah skrip.Dan kami juga meminta
env
untuk mengaturLD_LIBRARY_PATH
variabel lingkungan, yang merupakan satu-satunya titik peretasan.Perintah shell berakhir pada komentar yang dimulai dengan
#
sehingga shell mengabaikan tanda kutip tiga trailing'''
.sh
kemudian digantikan oleh contoh baru dari interpreter python yang membuka dan membaca skrip sumber python yang diberikan sebagai argumen pertama (the"$0"
).Python membuka file dan melompati baris pertama sumber berkat
-x
argumennya. Catatan: ini juga berfungsi tanpa-x
karena untuk Python shebang hanyalah sebuah komentar .Python kemudian menginterpretasikan baris ke-2 sebagai docstring untuk file modul saat ini, jadi jika Anda memerlukan docstring modul yang valid, cukup atur
__doc__
hal pertama dalam program python Anda seperti pada contoh di atas.sumber
''''exec ...
harus menyelesaikan pekerjaan. Catat tidak ada spasi sebelum exec atau itu akan membuatnya mencari perintah kosong. Anda ingin menyambungkan yang kosong ke arg pertama begitu$0
jugaexec
.Saya menemukan solusi yang agak bodoh ketika mencari executable yang mengecualikan script sebagai argumen tunggal:
sumber