Jestem w interesującej sytuacji, w której mam skrypt w języku Python, który teoretycznie może być uruchamiany przez różnych użytkowników w różnych środowiskach (i PATH) i na różnych systemach Linux. Chcę, aby ten skrypt był wykonywalny na tak wielu z nich, jak to możliwe bez sztucznych ograniczeń. Oto niektóre znane konfiguracje:
- Python 2.6 jest systemową wersją Pythona, więc wszystkie python, python2 i python2.6 istnieją w katalogu / usr / bin (i są równoważne).
- Python 2.6 to systemowa wersja Python, jak wyżej, ale Python 2.7 jest instalowany obok niego jako python2.7.
- Python 2.4 to systemowa wersja Python, której mój skrypt nie obsługuje. W / usr / bin mamy python, python2 i python2.4, które są równoważne, i python2.5, które obsługuje skrypt.
Chcę uruchomić ten sam wykonywalny skrypt Pythona na wszystkich trzech z nich. Byłoby miło, gdyby najpierw próbował użyć /usr/bin/python2.7, jeśli istnieje, to wróć do /usr/bin/python2.6, a następnie wróć do /usr/bin/python2.5, a następnie po prostu pomiń błąd, jeśli żaden z nich nie był obecny. Nie jestem jednak zbytnio rozłączony przy użyciu najnowszej możliwej wersji 2.x, pod warunkiem, że jest w stanie znaleźć jednego z poprawnych tłumaczy, jeśli jest obecny.
Moją pierwszą skłonnością była zmiana linii shebang z:
#!/usr/bin/python
do
#!/usr/bin/python2.[5-7]
ponieważ działa to dobrze w bash. Ale uruchomienie skryptu daje:
/usr/bin/python2.[5-7]: bad interpreter: No such file or directory
Okej, więc wypróbowuję następujące, które działa również w bash:
#!/bin/bash -c /usr/bin/python2.[5-7]
Ale znowu to się nie udaje w przypadku:
/bin/bash: - : invalid option
Okay, oczywiście mógłbym napisać osobny skrypt powłoki, który znajdzie właściwy interpreter i uruchomi skrypt Pythona za pomocą dowolnego znalezionego interpretera. Po prostu uważam za kłopot z dystrybucją dwóch plików, w których jeden powinien wystarczyć, pod warunkiem, że jest uruchomiony z zainstalowanym najnowszym interpreterem języka Python 2. Poproszenie ludzi o jawne wywołanie tłumacza (np. $ python2.5 script.py
) Nie wchodzi w grę. Poleganie na skonfigurowaniu PATH użytkownika w określony sposób również nie jest opcją.
Edytować:
Sprawdzanie wersji w skrypcie Python nie będzie działać, ponieważ używam instrukcji „z”, która istnieje od wersji Python 2.6 (i może być używana w wersji 2.5 z from __future__ import with_statement
). Powoduje to, że skrypt natychmiast kończy się niepowodzeniem z nieprzyjaznym dla użytkownika błędem SyntaxError i uniemożliwia mi kiedykolwiek sprawdzenie najpierw wersji i wygenerowanie odpowiedniego błędu.
Przykład: (spróbuj tego z interpreterem języka Python mniejszym niż 2.6)
#!/usr/bin/env python
import sys
print "You'll never see this!"
sys.exit()
with open('/dev/null', 'w') as out:
out.write('something')
import sys; sys.version_info()
aby sprawdzić, czy użytkownik ma wymaganą wersję Pythona../script.py
) Spowodowałoby wykonanie go przez python2.4, co spowodowałoby, że kod wykryłby, że jest to niewłaściwa wersja (i prawdopodobnie,). Ale istnieje doskonale dobry python2.5, który mógłby zostać użyty jako interpreter!exec
jeśli tak, w przeciwnym razie wydrukuj błąd.execve
). Argumenty to literały łańcuchowe, brak globowania, brak wyrażeń regularnych. Otóż to. Nawet jeśli pierwszy argument to „/ bin / bash”, a drugie opcje („-c ...”), te opcje nie są analizowane przez powłokę. Są one przekazywane bez przetworzenia do pliku wykonywalnego bash, dlatego otrzymujesz te błędy. Dodatkowo shebang działa tylko wtedy, gdy jest na początku. Obawiam się, że nie masz szczęścia (brakuje skryptu, który znajduje interpreter języka Python i podaje go TUTAJ, co brzmi jak okropny bałagan).Odpowiedzi:
Nie jestem ekspertem, ale uważam, że nie powinieneś określać dokładnej wersji Pythona do użycia i pozostawić ten wybór systemowi / użytkownikowi.
Powinieneś także użyć tej zamiast twardej ścieżki do Pythona w skrypcie:
lub
Jest zalecany przez Python doc we wszystkich wersjach:
W różnych dystrybucjach Python może być zainstalowany w różnych miejscach, więc
env
będzie go szukał wPATH
. Powinien być dostępny we wszystkich głównych dystrybucjach Linuksa oraz z tego, co widzę we FreeBSD.Skrypt powinien być wykonywany z tą wersją Pythona, która jest w twojej ŚCIEŻCE i która jest wybrana przez twoją dystrybucję *.
Jeśli twój skrypt jest kompatybilny ze wszystkimi wersjami Pythona oprócz 2.4, powinieneś po prostu sprawdzić, czy jest uruchomiony w Pythonie 2.4 i wydrukować informacje i wyjść.
Więcej do przeczytania
env
.Notatka
* W Gentoo istnieje narzędzie o nazwie
eselect
. Za jego pomocą możesz ustawić domyślne wersje różnych aplikacji (w tym Pythona) jako domyślne:źródło
W oparciu o kilka pomysłów z kilku komentarzy udało mi się stworzyć naprawdę brzydki hack, który wydaje się działać. Skrypt staje się skryptem bashowym, który otacza skrypt w języku Python i przekazuje go do interpretera języka Python za pośrednictwem „dokumentu tutaj”.
Na początku:
Kod Pythona idzie tutaj. Na samym końcu:
Gdy użytkownik uruchamia skrypt, do interpretacji pozostałej części skryptu jako dokumentu tutaj używana jest najnowsza wersja Pythona między 2.5 a 2.7.
Wyjaśnienie niektórych shenaniganów:
Dodane przeze mnie potrójne cytaty pozwalają również na import tego samego skryptu jako modułu Pythona (którego używam do celów testowych). Po zaimportowaniu przez Python wszystko pomiędzy pierwszym a drugim potrójnym pojedynczym cytatem jest interpretowane jako ciąg na poziomie modułu, a trzeci potrójny pojedynczy cytat jest komentowany. Reszta to zwykły Python.
Po uruchomieniu bezpośrednio (teraz jako skrypt bash) pierwsze dwa pojedyncze cudzysłowy stają się pustym ciągiem, a trzeci pojedynczy cudzysłów tworzy kolejny ciąg z czwartym pojedynczym cudzysłowem, zawierający tylko dwukropek. Ten ciąg interpretowany jest przez Bash jako brak możliwości działania. Cała reszta to składnia Bash do globowania plików binarnych Pythona w / usr / bin, wybierania ostatniego i uruchamiania exec, przekazując resztę pliku jako dokument tutaj. Niniejszy dokument zaczyna się od potrójnego pojedynczego cudzysłowu w języku Python zawierającego tylko znak krzyżyka / funta / oktotorpe. Pozostała część skryptu jest interpretowana normalnie, dopóki wiersz o treści „# EOF” nie zakończy dokumentu tutaj.
Czuję, że to jest przewrotne, więc mam nadzieję, że ktoś ma lepsze rozwiązanie.
źródło
# ft=python
.Linia shebang może określać tylko stałą ścieżkę do tłumacza. Jest
#!/usr/bin/env
sztuczka, by spojrzeć na tłumacza,PATH
ale to wszystko. Jeśli chcesz bardziej wyrafinować, musisz napisać kod powłoki otoki.Najbardziej oczywistym rozwiązaniem jest napisanie skryptu opakowania. Wywołaj skrypt Pythona
foo.real
i stwórz skrypt otokifoo
:Jeśli chcesz umieścić wszystko w jednym pliku, często możesz uczynić go poliglotą, która zaczyna się od
#!/bin/sh
linii (więc zostanie wykonana przez powłokę), ale jest również poprawnym skryptem w innym języku. W zależności od języka poliglota może być niemożliwy (#!
na przykład powoduje błąd składniowy). W Pythonie nie jest to bardzo trudne.(Cały tekst pomiędzy
'''
i'''
jest ciągiem Pythona na najwyższym poziomie, co nie ma żadnego efektu. W przypadku powłoki drugim wierszem jest''':'
po usunięciu cudzysłowów polecenie no-op:
.)źródło
# EOF
na końcu, jak w tej odpowiedzi . Twoje podejście jest w zasadzie takie samo, jak przedstawione tutaj .Ponieważ wymagania określają znaną listę plików binarnych, możesz to zrobić w Pythonie, wykonując następujące czynności. Nie działałoby to w porównaniu z jednocyfrową wersją Pythona, ale nie widzę tego w najbliższym czasie.
Uruchamia najwyższą wersję znajdującą się na dysku z uporządkowanej, rosnącej listy wersjonowanych pytonów, jeśli wersja oznaczona na pliku binarnym jest wyższa niż bieżąca wersja wykonywanego Pythona. Ważną częścią tego kodu jest „uporządkowana rosnąca lista wersji”.
Przepraszam za mój porysowany python
źródło
from __future__ import with_statement
, co musi być pierwszą rzeczą w skrypcie Python. Nie sądzę, żebyś wiedział, jak to zrobić podczas uruchamiania nowego tłumacza?with
jak zwykłego importu ?. Czy dodatkowa funkcjaif mypy == 25: from __future__ import with_statement
działa tuż przed „normalnymi sprawami”? Prawdopodobnie nie potrzebujesz if, jeśli nie wspierasz 2.4.Możesz napisać mały skrypt bash, który sprawdza dostępny plik wykonywalny phython i wywołuje go ze skryptem jako parametrem. Następnie możesz ustawić ten skrypt jako cel linii shebang:
A ten skrypt po prostu robi (po wyszukiwaniu):
Nie byłem pewien, czy jądro zaakceptuje ten pośredni skrypt, ale sprawdziłem i działa.
Edytuj 1
Aby ta zawstydzająca nieufność stała się dobrą propozycją:
Możliwe jest połączenie obu skryptów w jednym pliku. Po prostu piszesz skrypt Pythona jako dokument tutaj w skrypcie bash (jeśli zmienisz skrypt Pythona, musisz tylko ponownie skopiować skrypty razem). Albo tworzysz plik tymczasowy w np. / Tmp lub (jeśli python to obsługuje, nie wiem) podajesz skrypt jako dane wejściowe do interpretera:
źródło
$(ls /usr/bin/python?.? | tail -n1 )
ale nie udało mi się użyć tego sprytnie w shebang.