Bagaimana saya bisa menggunakan variabel lingkungan di shebang saya?

34

Saya memiliki skrip Python yang harus dijalankan dengan instalasi python tertentu. Apakah ada cara untuk membuat shebang agar bisa berjalan $FOO/bar/MyCustomPython?

eric.frederich
sumber

Jawaban:

29

Garis shebang sangat terbatas. Di bawah banyak varian unix (termasuk Linux), Anda hanya dapat memiliki dua kata: perintah dan satu argumen. Sering juga ada batasan panjang.

Solusi umum adalah dengan menulis pembungkus shell kecil. Beri nama skrip Python foo.py, dan letakkan skrip shell di sebelahnya foo.pydan panggil foo. Pendekatan ini tidak memerlukan header tertentu pada skrip Python.

#!/bin/sh
exec "$FOO/bar/MyCustomPython" "$0.py" "$@"

Pendekatan lain yang menggoda adalah menulis skrip wrapper seperti yang ada di atas, dan meletakkannya #!/path/to/wrapper/scriptsebagai garis shebang pada skrip Python. Namun, sebagian besar unices tidak mendukung perangkaian skrip shebang, jadi ini tidak akan berhasil.

Jika MyCustomPythonada di $PATH, Anda dapat menggunakannya envuntuk mencarinya:

#!/usr/bin/env MyCustomPython
import 

Namun pendekatan lain adalah mengatur agar skrip menjadi skrip shell yang valid (yang memuat penerjemah Python yang tepat pada dirinya sendiri) dan skrip yang valid dalam bahasa target (di sini Python). Ini mengharuskan Anda menemukan cara untuk menulis skrip dua bahasa untuk bahasa target Anda. Dalam Perl, ini dikenal sebagai if $running_under_some_shell.

#!/bin/sh
eval 'exec "$FOO/bar/MyCustomPerl" -wS $0 ${1+"$@"}'
    if $running_under_some_shell;
use 

Inilah salah satu cara untuk mencapai efek yang sama dengan Python. Dalam shell, "true"adalah trueutilitas, yang mengabaikan argumennya (dua string karakter tunggal :dan ') dan mengembalikan nilai sebenarnya. Dalam Python, "true"adalah string yang benar ketika diartikan sebagai boolean, jadi ini adalah ifinstruksi yang selalu benar dan mengeksekusi string literal.

#!/bin/sh
if "true" : '''\'
then
exec "$FOO/bar/MyCustomPython" "$0" "$@"
exit 127
fi
'''
import 

Kode Rosetta memiliki skrip dua bahasa seperti itu dalam beberapa bahasa lain.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
@ Chris: semuanya ada di dalam tanda kutip tunggal. Tolong jelaskan ...
Stéphane Gimenez
1
Anda penyihir berdarah ... Ini bagus. Saya menggunakan ini untuk mengedit PYTHONPATHvariabel env untuk memuat modul dari lokasi non-standar untuk skrip CGI pada sistem saya tidak memiliki akses root. Penolong. Terima kasih lagi.
wspurgin
12

Baris Shebang tidak mengalami ekspansi variabel, jadi Anda tidak dapat menggunakan $FOO/MyCustomPythonkarena akan mencari executable bernama dollar-FOO -...

Alternatifnya adalah memiliki titik shebang Anda ke skrip shell sebagai penerjemah, dan skrip shell ini kemudian dapat menggunakan variabel lingkungan untuk menemukan yang benar dan mengeksekusinya.

Contoh: buat mypython.shskrip di /usr/local/bin(atau direktori lain di Anda $PATH), dengan konten berikut:

#! /bin/sh
PYTHON="$FOO/bar/MyCustomPython"
exec "$PYTHON" "$@"

maka Anda dapat menggunakan baris shebang ini untuk menjalankan skrip Python MyCustomPythonmelalui mypython.sh:

#!/usr/bin/env mypython.sh
Riccardo Murri
sumber
6
Gunakan $python, bukan $PYTHON- berdasarkan konvensi, kami mengkapitalkan variabel lingkungan (PAGER, EDITOR, SHELL ... $PYTHONdalam skrip Anda bukan variabel lingkungan) dan variabel shell internal (BASH_VERSION, RANDOM, ...). Semua nama variabel lainnya harus mengandung setidaknya satu huruf kecil. Konvensi ini menghindari variabel lingkungan dan internal yang secara tidak sengaja diabaikan. Selain itu, jangan gunakan ekstensi untuk skrip Anda. Script mendefinisikan perintah baru yang dapat Anda jalankan, dan perintah umumnya tidak diberikan ekstensi.
Chris Down
4

Anda bisa menggunakan jalur absolut ke instalasi python khusus, atau Anda bisa memasukkannya ke dalam $PATHdan menggunakan Anda #!/usr/bin/env [command]. Kalau tidak, tulis pembungkus untuk itu dan gunakan execuntuk mengganti gambar proses, sebagai contoh:

#!/bin/bash
exec "$ENV/python" "$@"
Chris Down
sumber
3

Pertama, tentukan bagaimana versi Python Anda berbeda dari Python standar yang sudah diinstal (misalnya, modul tambahan) dan kemudian pada awal program 'aktifkan' bagaimana Python disebut:

#!/usr/bin/python
import os
import sys
try:
    import MyCustomModule
except ImportError:
    # only need to use expandvar if you know the string below has an envvar
    custprog = os.path.expandvar("$FOO/bar/MyCustomPython")
    os.execv(custprog, sys.argv) # call alternate python with the same arguments
    raise SystemExit('could not call %s' % custprog)
# if we get here, then we have the correct python interpreter

Ini harus memungkinkan program dipanggil oleh instance Python lainnya. Saya melakukan sesuatu yang mirip untuk contoh dengan built-in sql library yang tidak dapat diimpor sebagai modul di python sistem.

Arcege
sumber
0

Jika Anda dapat mengganti $FOOdi $FOO/bar/MyCustomPythondengan pilihan jalan beton, Anda dapat memberitahu envgaris peristiwa di mana untuk mencari kustom versi Python Anda dengan langsung menetapkan kustom PATHdi dalamnya.

#!/usr/bin/env PATH="/path1/to/MyCustomPython:/path2/to/MyCustomPython" python

Sunting : Tampaknya hanya berfungsi tanpa tanda kutip di sekitar penetapan nilai PATH:

#!/usr/bin/env PATH=/path1/to/MyCustomPython:/path2/to/MyCustomPython python
chan
sumber
5
Hati-hati karena ini tidak berfungsi pada semua varian unix (mis. Tidak di Linux). Dan Anda menghapus semua direktori yang ada dari jalur, yang kemungkinan akan menyebabkan masalah di telepon.
Gilles 'SO- stop being evil'
1
Lebih baik kembangkan PATHseperti ini:PATH=$PATH:/path/to/whatever...
Bengt