Apakah ada cara untuk secara dinamis memilih penerjemah yang menjalankan skrip? Saya memiliki skrip yang saya jalankan di dua sistem yang berbeda, dan juru bahasa yang ingin saya gunakan terletak di lokasi yang berbeda di kedua sistem. Apa yang saya akhirnya harus mengubah baris hashbang setiap kali saya beralih. Saya ingin melakukan sesuatu yang setara logis dengan ini (saya menyadari bahwa konstruksi yang tepat ini tidak mungkin):
if running on system A:
#!/path/to/python/on/systemA
elif running on system B:
#!/path/on/systemB
#Rest of script goes here
Atau yang lebih baik adalah ini, sehingga ia mencoba menggunakan juru bahasa pertama, dan jika tidak menemukannya menggunakan yang kedua:
try:
#!/path/to/python/on/systemA
except:
#!path/on/systemB
#Rest of script goes here
Jelas, saya bisa menjalankannya sebagai
/path/to/python/on/systemA myscript.py
atau
/path/on/systemB myscript.py
tergantung di mana saya berada, tetapi saya benar-benar memiliki skrip pembungkus yang diluncurkan myscript.py
, jadi saya ingin menentukan jalur ke juru bahasa python secara terprogram secara terprogram daripada dengan tangan.
if
kondisinya bukan pilihan bagi Anda? seperti,if something; then /bin/sh restofscript.sh elif...
Jawaban:
Tidak, itu tidak akan berhasil. Dua karakter
#!
mutlak harus menjadi dua karakter pertama dalam file (bagaimana Anda menentukan apa yang menafsirkan pernyataan-if?). Ini merupakan "angka ajaib" yangexec()
dideteksi oleh keluarga fungsi ketika mereka menentukan apakah file yang akan mereka eksekusi adalah skrip (yang membutuhkan penerjemah) atau file biner (yang tidak).Format garis shebang cukup ketat. Perlu memiliki jalur absolut ke penerjemah dan paling banyak satu argumen untuk itu.
Yang dapat Anda lakukan adalah menggunakan
env
:Sekarang, jalan menuju
env
adalah biasanya/usr/bin/env
, tapi secara teknis itu bukan jaminan.Hal ini memungkinkan Anda untuk menyesuaikan
PATH
variabel lingkungan pada setiap sistem sehinggainterpreter
(baik itubash
,python
atauperl
atau apa pun yang Anda miliki) ditemukan.Kelemahan dari pendekatan ini adalah bahwa tidak mungkin untuk menyampaikan argumen kepada penerjemah dengan mudah.
Ini artinya
dan
tidak mungkin bekerja pada beberapa sistem.
Pendekatan lain yang jelas adalah dengan menggunakan autotool GNU (atau sistem templating sederhana) untuk menemukan penerjemah dan menempatkan jalur yang benar ke dalam file dalam
./configure
langkah, yang akan dijalankan saat menginstal skrip pada setiap sistem.Seseorang juga dapat menggunakan skrip dengan penerjemah eksplisit, tapi itu jelas yang ingin Anda hindari:
sumber
#!
perlu datang di awal, karena itu bukan shell yang memproses garis itu. Saya bertanya-tanya apakah ada cara untuk menempatkan logika di dalam baris hashbang yang akan setara dengan if / else. Saya juga berharap untuk tidak bermain-main dengan sayaPATH
tetapi saya kira itu adalah satu-satunya pilihan saya.#!/usr/bin/awk
, Anda dapat memberikan satu argumen, sebagai#!/usr/bin/awk -f
. Jika biner yang Anda tuju adalahenv
, argumennya adalah biner yang Anda mintaenv
cari, seperti dalam#!/usr/bin/env awk
./usr/bin/env
dengan argumen tunggalawk -f
.foo.awk
hashbang line#!/usr/bin/env awk -f
dan menyebutnya dengan./foo.awk
itu, di Linux, yangenv
dilihat adalah dua parameterawk -f
dan./foo.awk
. Ini benar-benar pergi mencari/usr/bin/awk -f
(dll.) Dengan spasi.Anda selalu dapat membuat skrip wrapper untuk menemukan juru bahasa yang benar untuk program yang sebenarnya:
Simpan pembungkus di pengguna
PATH
sebagaiprogram
dan sisihkan program yang sebenarnya atau dengan nama lain.Saya menggunakan
#!/bin/bash
hashbang karenaflags
array. Jika Anda tidak perlu menyimpan sejumlah flag atau semacamnya dan dapat melakukannya tanpa itu, skrip harus bekerja dengan baik#!/bin/sh
.sumber
exec "$interpreter" "${flags[@]}" "$script" "$@"
juga digunakan untuk menjaga proses tree cleaner. Itu juga menyebarkan kode keluar.exec
.#!/bin/sh
lebih baik#!/bin/bash
? Bahkan jika/bin/sh
symlink ke shell yang berbeda, itu harus ada pada sebagian besar (jika tidak semua) * sistem nix, ditambah itu akan memaksa penulis skrip untuk membuat skrip portabel daripada jatuh ke bashism.flags
adalah fitur non-standar, tetapi cukup berguna untuk menyimpan sejumlah flag, jadi saya memutuskan untuk menyimpannya.script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@"
. Anda juga bisa menambahkan pengecekan error untuk kegagalan exec.Anda juga dapat menulis polyglot (menggabungkan dua bahasa). / bin / sh dijamin ada.
Ini memiliki kelemahan dari kode jelek dan mungkin beberapa
/bin/sh
s berpotensi menjadi bingung. Tetapi dapat digunakan ketikaenv
tidak ada atau ada di tempat lain selain / usr / bin / env. Ini juga dapat digunakan jika Anda ingin melakukan beberapa pilihan yang cukup mewah.Bagian pertama dari skrip menentukan penerjemah mana yang akan digunakan ketika dijalankan dengan / bin / sh sebagai penerjemah, tetapi diabaikan ketika dijalankan oleh penerjemah yang benar. Gunakan
exec
untuk mencegah shell dari menjalankan lebih dari bagian pertama.Contoh python:
sumber
exec "$interpreter" "$0" "$@"
mendapatkan nama skrip itu sendiri ke penerjemah yang sebenarnya juga. (Dan kemudian berharap tidak ada yang berbohong ketika mengatur$0
.)#!
, Scala mengabaikan semuanya hingga pencocokan!#
; ini memungkinkan Anda untuk menempatkan kode skrip yang rumit secara sewenang-wenang dalam bahasa yang sewenang-wenang di sana, dan kemudianexec
mesin eksekusi Scala dengan skrip.Saya lebih suka jawaban Kusalananda dan ilkkachu, tetapi di sini ada alternatif jawaban yang lebih langsung melakukan apa yang ditanyakan, hanya karena ditanyakan.
Perhatikan bahwa Anda hanya dapat melakukan ini ketika penerjemah mengizinkan penulisan kode dalam argumen pertama. Di sini,
-e
dan semuanya setelah itu diambil kata demi kata sebagai 1 argumen untuk ruby. Sejauh yang saya tahu, Anda tidak dapat menggunakan bash untuk kode shebang, karenabash -c
memerlukan kode dalam argumen yang terpisah.Saya mencoba melakukan hal yang sama dengan python untuk kode shebang:
tapi ternyata terlalu lama dan linux (setidaknya di komputer saya) memotong shebang menjadi 127 karakter. Maafkan penggunaan
exec
untuk memasukkan baris baru karena python tidak mengizinkan try-exception atauimport
s tanpa baris baru.Saya tidak yakin seberapa portabel ini, dan saya tidak akan melakukannya pada kode yang dimaksudkan untuk didistribusikan. Namun demikian, itu bisa dilakukan. Mungkin seseorang akan menemukannya berguna untuk debugging cepat dan kotor atau sesuatu.
sumber
Meskipun ini tidak memilih juru bahasa dalam skrip shell (itu memilihnya per mesin) itu adalah alternatif yang lebih mudah jika Anda memiliki akses administratif ke semua mesin yang Anda coba jalankan skripnya.
Buat symlink (atau hardlink jika diinginkan) untuk menunjuk ke jalur juru bahasa yang diinginkan. Sebagai contoh, pada sistem saya perl dan python berada di / usr / bin:
akan membuat symlink untuk memungkinkan hashbang untuk menyelesaikan / bin / perl, dll. Ini menjaga kemampuan untuk meneruskan parameter ke skrip juga.
sumber
Saya dihadapkan dengan masalah yang sama seperti ini hari ini (
python3
menunjuk ke versi python yang terlalu tua pada satu sistem), dan muncul dengan pendekatan yang sedikit berbeda dari yang dibahas di sini: Gunakan versi "salah" dari python untuk bootstrap ke yang "benar". Batasannya adalah bahwa beberapa versi python harus dapat dijangkau dengan andal, tetapi itu biasanya dapat dicapai dengan misalnya#!/usr/bin/env python3
.Jadi yang saya lakukan adalah memulai skrip saya dengan:
Apa yang dilakukan adalah:
sumber