Jak mogę używać zmiennych środowiskowych w moim shebang?


34

Mam skrypt w języku Python, który należy uruchomić z określoną instalacją w języku Python. Czy istnieje sposób na wykonanie shebang, aby działał $FOO/bar/MyCustomPython?

Odpowiedzi:


29

Linia shebang jest bardzo ograniczona. W wielu wariantach uniksowych (w tym Linux) możesz mieć tylko dwa słowa: polecenie i pojedynczy argument. Często występuje także ograniczenie długości.

Ogólnym rozwiązaniem jest napisanie małego opakowania powłoki. Nazwij skrypt Pythona foo.py, umieść skrypt powłoki foo.pyi wywołaj go foo. To podejście nie wymaga żadnego konkretnego nagłówka w skrypcie Python.

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

Innym kuszącym podejściem jest napisanie skryptu opakowującego, takiego jak powyższy, i umieszczenie go #!/path/to/wrapper/scriptjako linii shebang w skrypcie Python. Jednak większość jednorożców nie obsługuje tworzenia łańcuchów skryptów shebang, więc to nie zadziała.

Jeśli MyCustomPythonbył w $PATH, możesz to envsprawdzić:

#!/usr/bin/env MyCustomPython
import 

Jeszcze innym podejściem jest skonfigurowanie skryptu, aby był zarówno poprawnym skryptem powłoki (który ładuje odpowiedni interpreter języka Python), jak i poprawnym skryptem w języku docelowym (tutaj Python). Wymaga to znalezienia sposobu na napisanie takiego dwujęzycznego skryptu dla języka docelowego. W Perlu jest to znane jako if $running_under_some_shell.

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

Oto jeden ze sposobów osiągnięcia tego samego efektu w Pythonie. W powłoce "true"znajduje się truenarzędzie, które ignoruje argumenty (dwa ciągi jednoznakowe :i ') i zwraca prawdziwą wartość. W Pythonie "true"łańcuch jest prawdziwy, gdy jest interpretowany jako wartość logiczna, więc jest to ifinstrukcja, która zawsze jest prawdziwa i wykonuje literał łańcucha.

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

Kod Rosetta ma takie dwujęzyczne skrypty w kilku innych językach.


1
@Chris: wszystko jest w pojedynczych cudzysłowach. Proszę wyjaśnić…
Stéphane Gimenez,

1
Jesteś cholernym czarodziejem ... To świetnie. Użyłem tego do edycji PYTHONPATHzmiennej env, aby załadować moduły ze niestandardowej lokalizacji dla skryptu CGI w systemie, do którego nie miałem dostępu do roota. Oszczędzanie życia Dzięki jeszcze raz.
wspurgin

12

Linie Shebang nie podlegają rozszerzaniu zmiennemu, więc nie można użyć, $FOO/MyCustomPythonponieważ szukałby pliku wykonywalnego o nazwie dollar-FOO -...

Alternatywą jest, aby shebang wskazywał na skrypt powłoki jako interpreter, a ten skrypt powłoki może następnie użyć zmiennych środowiskowych do zlokalizowania poprawnego i wykonania go.

Przykład: utwórz mypython.shskrypt w /usr/local/bin(lub innym katalogu na swoim $PATH), o następującej treści:

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

możesz użyć tej linii shebang, aby wykonać skrypt w języku Python MyCustomPythonpoprzez mypython.sh:

#!/usr/bin/env mypython.sh

6
Używaj $python, a nie $PYTHON- zgodnie z konwencją, kapitalizujemy zmienne środowiskowe (PAGER, EDITOR, SHELL ... $PYTHONw twoim skrypcie nie jest zmienną środowiskową) i wewnętrzne zmienne powłoki (BASH_VERSION, RANDOM, ...). Wszystkie pozostałe nazwy zmiennych powinny zawierać co najmniej jedną małą literę. Konwencja ta pozwala uniknąć przypadkowego zastąpienia zmiennych środowiskowych i wewnętrznych. Nie używaj również rozszerzeń do swoich skryptów. Skrypty definiują nowe polecenia, które można uruchamiać, a na ogół polecenia nie mają rozszerzeń.
Chris Down,

4

Możesz albo użyć bezwzględnej ścieżki do niestandardowej instalacji Pythona, albo umieścić ją w swojej $PATHi używać #!/usr/bin/env [command]. W przeciwnym razie napisz dla niego opakowanie i użyj execdo zastąpienia obrazu procesu, na przykład:

#!/bin/bash
exec "$ENV/python" "$@"

3

Najpierw określ, w jaki sposób Twoja wersja Python różni się od standardowego już zainstalowanego Pythona (na przykład dodatkowy moduł), a następnie na początku programu „przełącz”, jak nazywa się Python:

#!/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

Powinno to umożliwić wywołanie programu przez dowolną inną instancję Pythona. Robię coś podobnego dla instancji z wbudowaną biblioteką SQL, której nie można zaimportować jako modułu w pythonie systemu.


0

Jeśli można zastąpić $FOOw $FOO/bar/MyCustomPythonkonkretnych opcji ścieżek, można powiedzieć envshebang linię gdzie patrzeć niestandardową wersję Pythona bezpośrednio przez ustawienie niestandardowe PATHw nim.

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

Edycja : Wydaje się, że działa tylko bez cudzysłowów wokół przypisania wartości PATH:

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

5
Uważaj, że to nie działa na wszystkich wariantach Uniksa (np. Nie na Linuksie). I usuwasz wszystkie istniejące katalogi ze ścieżki, co może powodować problemy w dalszej kolejności.
Gilles „SO- przestań być zły”

1
Lepiej rozwinąć w PATHten sposób:PATH=$PATH:/path/to/whatever...
Bengt
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.