Dlaczego ludzie piszą #! / Usr / bin / env shebang python w pierwszym wierszu skryptu Python?


1046

Wydaje mi się, że pliki działają tak samo bez tej linii.


1
Poniższa odpowiedź mówi, że jest to tylko wiersz komentarza. Nie zawsze tak jest. Mam „Witaj, świecie!” Skrypt CGI (.py), który będzie uruchamiał i wyświetlał stronę internetową tylko #!/usr/bin/env pythonu góry.
Chakotay,


Mogą biec, ale nie w zamierzonym środowisku
Nicholas Hamilton

18
Odwiedziłem ten post tyle razy w ciągu 7 lat, ponieważ czasami zapominam hashbang env. Skopiuj makaron :)
BugHunterUK

Odpowiedzi:


1082

Jeśli masz zainstalowanych kilka wersji Pythona, /usr/bin/envupewnij się, że użyty interpreter będzie pierwszym w twoim środowisku $PATH. Alternatywą byłoby hardcode coś takiego #!/usr/bin/python; to jest ok, ale mniej elastyczne.

W Uniksie plik wykonywalny , który ma być interpretowany, może wskazywać, jakiego interpretera użyć, umieszczając #!na początku pierwszego wiersza, a za nim interpreter (i wszelkie potrzebne flagi).

Jeśli mówisz o innych platformach, oczywiście ta zasada nie ma zastosowania (ale ta „linia shebang” nie szkodzi i pomoże, jeśli kiedykolwiek skopiujesz ten skrypt na platformę z bazą Unix, taką jak Linux, Mac itp.).


267
Wystarczy dodać: dotyczy to uruchamiania go w systemie Unix poprzez uczynienie go wykonywalnym ( chmod +x myscript.py), a następnie uruchamianie bezpośrednio ./myscript.py:, a nie tylko python myscript.py.
Craig McQueen,

28
użycie envdaje maksymalną elastyczność, ponieważ użytkownik może wybrać tłumacza, który będzie używany, zmieniając ŚCIEŻKĘ. Często ta elastyczność nie jest wymagana, ale wadą jest to, że na przykład Linux nie może użyć nazwy skryptu dla nazwy procesu psi powraca do „python”. Na przykład przy pakowaniu aplikacji Pythona dla dystrybucji radzę nie używać env.
pixelbeat

9
pyprogram uruchamiający może korzystać z linii shebang w systemie Windows. Jest zawarty w Pythonie 3.3 lub można go zainstalować niezależnie .
jfs

6
Ważne słowo ostrzeżenia, wartość zwracana env ostatecznie wygasa. Co raczej nie wpłynie na ciebie, jeśli prowadzisz krótkotrwałe procesy. Jednak /usr/bin/env: Key has expiredpo wielu godzinach proces umierał z tą wiadomością .
malaverdiere

4
@malaverdiere czy możesz połączyć się z jakimikolwiek zasobami, które wyjaśniają to zachowanie związane z wygaśnięciem? Nie mogę ich znaleźć.
Michael

266

To się nazywa linia shebang . Jak wyjaśnia wpis w Wikipedii :

W informatyce shebang (zwany również hashbangiem, hashplingiem, funtem lub crunchbangiem) odnosi się do znaków „#!” gdy są to pierwsze dwa znaki w dyrektywie interpretera jako pierwsza linia pliku tekstowego. W systemie operacyjnym uniksopodobnym moduł ładujący pobiera obecność tych dwóch znaków jako wskazanie, że plik jest skryptem, i próbuje wykonać ten skrypt za pomocą interpretera określonego przez resztę pierwszego wiersza pliku.

Zobacz także wpis FAQ Unix .

Nawet w systemie Windows, w którym linia shebang nie określa uruchomionego interpretera, można przekazać opcje do interpretera, określając je w linii shebang. Uważam, że przydatne jest utrzymywanie ogólnej linii shebang w jednorazowych skryptach (takich jak te, które piszę, odpowiadając na pytania dotyczące SO), dzięki czemu mogę szybko przetestować je zarówno w systemie Windows, jak i ArchLinux .

Narzędzie env pozwala na wywołanie polecenia na ścieżce:

Pierwszy pozostały argument określa nazwę programu do wywołania; jest wyszukiwane zgodnie ze PATHzmienną środowiskową. Wszelkie pozostałe argumenty są przekazywane jako argumenty do tego programu.


30
Łatwy do znalezienia w Google - jeśli ktoś zna słowa kluczowe (niezbędna jest „linia shebang”).
Arafangion

14
W rzeczywistości to wyjaśnienie jest jaśniejsze niż w przypadku innych referencji sprawdzonych przez Google. Zawsze lepiej jest uzyskać wyjaśnienie 1 akapitu ukierunkowane na pytanie, niż przeczytać całą instrukcję dotyczącą każdego potencjalnego zastosowania.
Sam Goldberg,

1
@Arafangion prawdopodobnie znajdziesz to pytanie użyteczne. TL; DR: symbolhound.com
ulidtko

@ulidtko: Ciekawa wyszukiwarka, zastanów się nad napisaniem odpowiedzi, aby pytanie Johna Garciasa miało lepszą odpowiedź.
Arafangion,

1
„Nawet w systemie Windows, w którym linia shebang nie określa uruchomionego interpretera, możesz przekazać opcje tłumaczowi, określając je w linii shebang”. To jest po prostu fałsz; jeśli coś takiego się zdarzy, to dlatego, że sam tłumacz przetwarza linię shebang. Jeśli tłumacz nie ma specjalnego rozpoznania dla linii shebang, nic takiego się nie dzieje. Windows nic nie robi z liniami shebang. ”W tym przypadku możesz opisać program uruchamiający python: python.org/dev/peps/pep-0397 .
Kaz

154

Rozwijając nieco inne odpowiedzi, oto mały przykład tego, jak skrypty wiersza poleceń mogą wpaść w kłopoty przez nieumyślne użycie /usr/bin/envlinii shebang:

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

Moduł json nie istnieje w Pythonie 2.5.

Jednym ze sposobów ochrony przed tego rodzaju problemami jest użycie wersjonowanych nazw poleceń python, które są zwykle instalowane z większością Pythonów:

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Jeśli potrzebujesz tylko odróżnić Python 2.x i Python 3.x, najnowsze wydania Python 3 również zawierają python3nazwę:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")

27
Hmm, nie to wyciągnąłem z tego postu.
glenn jackman

1
Różnica między lokalną a globalną. Jeśli which pythonwróci /usr/bin/python, lokalna ścieżka do katalogu może być ciężko kodowane: #!/usr/bin/python. Ale jest to mniej elastyczne niż #!/usr/bin/env pythonma to zastosowanie globalne.
noobninja

85

Aby uruchomić skrypt Pythona, musimy powiedzieć powłoce trzy rzeczy:

  1. Że plik jest skryptem
  2. Który interpreter chcemy wykonać skrypt
  3. Ścieżka wspomnianego tłumacza

Shebang #!wykonuje (1.). Shebang zaczyna się od, #ponieważ #znak jest znacznikiem komentarza w wielu językach skryptowych. Treść linii shebang jest zatem automatycznie ignorowana przez tłumacza.

W envrealizuje polecenie (2) i (3). Cytując „grawitację”,

Typowym zastosowaniem tego envpolecenia jest uruchamianie interpreterów, wykorzystując fakt, że env przeszuka $ PATH dla polecenia, które ma zostać uruchomione. Ponieważ linia shebang wymaga podania bezwzględnej ścieżki, a lokalizacja różnych interpretatorów (perl, bash, python) może się bardzo różnić, często używa się:

#!/usr/bin/env perl  zamiast zgadywać, czy jest to / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl lub / home / MrDaniel / usr / bin / perl w systemie użytkownika ...

Z drugiej strony, env prawie zawsze znajduje się w / usr / bin / env. (Z wyjątkiem przypadków, gdy tak nie jest; niektóre systemy mogą używać / bin / env, ale jest to dość rzadka okazja i zdarza się tylko w systemach innych niż Linux).


1
„grawitacja” gdzie?
Pacerier

44

Być może twoje pytanie brzmi w tym sensie:

Jeśli chcesz użyć: $python myscript.py

W ogóle nie potrzebujesz tej linii. System wywoła Python, a następnie interpreter Pythona uruchomi skrypt.

Ale jeśli zamierzasz użyć: $./myscript.py

Wywołując go bezpośrednio jak zwykły program lub skrypt bash, musisz napisać ten wiersz, aby określić w systemie, którego programu użyć, aby go uruchomić (a także umożliwić wykonanie go za pomocą chmod 755)


lub możesz napisać python3 myscript.py
vijay shanker

44

execWywołanie systemowe jądra Linux rozumie shebangs ( #!) natywnie

Kiedy robisz na bash:

./something

w Linuksie wywołuje to wywołanie execsystemowe ze ścieżką ./something.

Ta linia jądra zostaje wywołana w pliku przekazanym do exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Odczytuje pierwsze bajty pliku i porównuje je #!.

Jeśli porównanie jest prawdziwe, reszta linii jest analizowana przez jądro Linuksa, które wykonuje kolejne execwywołanie ze ścieżką /usr/bin/env pythoni bieżącym plikiem jako pierwszym argumentem:

/usr/bin/env python /path/to/script.py

i działa to na każdy język skryptowy, który używa #jako znaku komentarza.

I tak, możesz wykonać nieskończoną pętlę za pomocą:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash rozpoznaje błąd:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! akurat jest czytelny dla ludzi, ale nie jest to wymagane.

Jeśli plik zaczął się od różnych bajtów, execwywołanie systemowe użyłoby innej procedury obsługi. Innym najważniejszym wbudowanym modułem obsługi są pliki wykonywalne ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, który sprawdza bajty 7f 45 4c 46(które również są ludźmi) czytelne dla .ELF). Potwierdźmy to, czytając 4 pierwsze bajty /bin/ls, czyli plik wykonywalny ELF:

head -c 4 "$(which ls)" | hd 

wynik:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Więc kiedy jądro zobaczy te bajty, pobiera plik ELF, poprawnie zapisuje go w pamięci i rozpoczyna z nim nowy proces. Zobacz także: W jaki sposób jądro uzyskuje wykonywalny plik binarny działający pod Linuksem?

Na koniec możesz dodać własne binfmt_miscmechanizmy shebang za pomocą mechanizmu. Na przykład możesz dodać niestandardowy moduł obsługi .jarplików . Ten mechanizm obsługuje nawet programy obsługi według rozszerzeń plików. Inną aplikacją jest przezroczyste uruchamianie plików wykonywalnych o innej architekturze za pomocą QEMU .

Nie sądzę jednak, że POSIX określa shebangs: https://unix.stackexchange.com/a/346214/32558 , chociaż wspomina o tym w sekcjach uzasadnienia oraz w formie „jeśli skrypty wykonywalne są obsługiwane przez system, coś może zdarzyć". Wydaje się jednak, że macOS i FreeBSD go implementują.

PATH motywacja do wyszukiwania

Prawdopodobnie jedną z głównych motywów istnienia shebangów jest fakt, że w Linuksie często chcemy uruchamiać polecenia z PATH:

basename-of-command

zamiast:

/full/path/to/basename-of-command

Ale bez mechanizmu shebang skąd Linux wiedziałby, jak uruchomić każdy typ pliku?

Kodowanie rozszerzenia w poleceniach:

 basename-of-command.py

lub wdrożenie wyszukiwania PATH na każdym tłumaczu:

python basename-of-command

byłaby możliwa, ale to ma główny problem, że wszystko się psuje, jeśli kiedykolwiek zdecydujemy się zmienić polecenie na inny język.

Shebangi pięknie rozwiązują ten problem.


39

Technicznie rzecz biorąc, w Pythonie jest to tylko wiersz komentarza.

Ten wiersz jest używany tylko wtedy, gdy uruchomisz skrypt py z powłoki (z wiersza poleceń). Nazywa się to Shebang !” i jest używany w różnych sytuacjach, nie tylko ze skryptami Python.

Tutaj instruuje powłokę, aby uruchomiła określoną wersję Pythona (aby zająć się resztą pliku.


Shebang to koncepcja uniksowa. Warto wspomnieć, że działa również w systemie Windows, jeśli masz zainstalowany program uruchamiający Python py.exe . Jest to część standardowej instalacji Pythona.
florisla

38

Głównym powodem tego jest uczynienie skryptu przenośnym w środowiskach systemu operacyjnego.

Na przykład w mingw skrypty python używają:

#!/c/python3k/python 

a w dystrybucji GNU / Linux jest to albo:

#!/usr/local/bin/python 

lub

#!/usr/bin/python

w ramach najlepszego komercyjnego systemu Uni / SW ze wszystkich (OS / X) jest to:

#!/Applications/MacPython 2.5/python

lub na FreeBSD:

#!/usr/local/bin/python

Jednak wszystkie te różnice mogą sprawić, że skrypt będzie przenośny we wszystkich przy użyciu:

#!/usr/bin/env python

2
Pod MacOSX tak też jest /usr/bin/python. Pod Linuksem Python zainstalowany przez system jest również prawie na pewno /usr/bin/python(nigdy nie widziałem nic innego i nie miałoby to sensu). Pamiętaj, że mogą istnieć systemy, które nie mają /usr/bin/env.
Albert,

1
Jeśli korzystasz z OSX i korzystasz z Homebrew i postępujesz zgodnie z domyślnymi instrukcjami instalacji, będzie on pod #! / Usr / local / bin / python
będzie

@ Jean-PaulCalderone: Zobacz odpowiedź saaj poniżej.
pydsigner

Aktualizacja na rok 2018: Bare pythonnie jest tak przenośny, to domyślny interpreter Pythona w dystrybucji. Arch Linux domyślnie
używa

22

Prawdopodobnie sensowne jest podkreślenie jednej rzeczy, której większość przeoczyła, co może uniemożliwić natychmiastowe zrozumienie. Kiedy piszeszpython w terminalu zwykle nie podajesz pełnej ścieżki. Zamiast tego plik wykonywalny jest sprawdzany w PATHzmiennej środowiskowej. Z kolei, jeśli chcesz bezpośrednio uruchomić program w języku Python /path/to/app.py, musisz powiedzieć powłoce, jakiego interpretera ma użyć (za pomocą skrótu , co inni współautorzy wyjaśniają powyżej).

Hashbang oczekuje pełnej ścieżki do tłumacza. Aby więc bezpośrednio uruchomić program w języku Python, musisz podać pełną ścieżkę do pliku binarnego w języku Python, który różni się znacznie, szczególnie biorąc pod uwagę użycie virtualenv . Aby rozwiązać problem przenośności, zastosowano sztuczkę /usr/bin/env. Ten ostatni ma pierwotnie na celu zmianę środowiska w miejscu i uruchomienie w nim polecenia. Jeśli nie wprowadzono żadnych zmian, uruchamia polecenie w bieżącym środowisku, co skutecznie skutkuje tym samym PATHwyszukiwaniem, które załatwia sprawę .

Źródło z unix stackexchange


14

Jest to konwencja powłoki, która informuje powłokę, który program może wykonać skrypt.

#! / usr / bin / env python

rozwiązuje ścieżkę do pliku binarnego Python.



9

Możesz spróbować rozwiązać ten problem, używając virtualenv

Oto test.py

#! /usr/bin/env python
import sys
print(sys.version)

Twórz środowiska wirtualne

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

aktywuj każde środowisko, a następnie sprawdź różnice

echo $PATH
./test.py

9

Określa tylko jakiego interpretera chcesz użyć. Aby to zrozumieć, utwórz plik przez terminal, wykonując touch test.py, a następnie wpisz w tym pliku następujące czynności:

#!/usr/bin/env python3
print "test"

i zrobić, chmod +x test.pyaby skrypt był wykonywalny. Po tym ./test.pypowinieneś otrzymać komunikat o błędzie:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

ponieważ python3 nie obsługuje operatora drukowania.

Teraz idź dalej i zmień pierwszą linię kodu na:

#!/usr/bin/env python2

i będzie działać, drukując testna standardowe wyjście, ponieważ python2 obsługuje operator drukowania. Teraz nauczyłeś się przełączać między interpretatorami skryptów.


9

Wydaje mi się, że pliki działają tak samo bez tej linii.

Jeśli tak, to może używasz programu Python w systemie Windows? Windows nie używa tej linii - zamiast tego używa rozszerzenia nazwy pliku do uruchomienia programu skojarzonego z tym rozszerzeniem.

Jednak w 2011 r. Opracowano „program uruchamiający w języku Python”, który (do pewnego stopnia) naśladuje zachowanie systemu Linux w systemie Windows. Jest to ograniczone tylko do wyboru, który interpreter Pythona ma być uruchomiony - np. Do wyboru między Pythonem 2 a Pythonem 3 w systemie, w którym oba są zainstalowane. Program uruchamiający jest opcjonalnie instalowany jak py.exepodczas instalacji w języku Python i może być powiązany z .pyplikami, dzięki czemu program uruchamiający sprawdzi tę linię, a następnie uruchomi określoną wersję interpretera języka Python.


6
On również może używać $ python myscript.py.
Sinan Ünür

Popełniłem błąd, nie mając linii i użyłem python script.py, i pewnego dnia po prostu zrobiłem ./myscript.py i wszystko przestało działać, a potem zdałem sobie sprawę, że system szuka pliku jako skryptu powłoki zamiast skryptu python.
Guagua

8

Jest to bardziej informacja historyczna niż „prawdziwa” odpowiedź.

Pamiętaj, że w dawnych czasach pan miał wiele systemów operacyjnych UNIX jak projektanci, których wszyscy mieli swoje własne pojęcie gdzie umieścić rzeczy, a czasami nie obejmują Python, Perl, Bash, lub wiele innych GNU / Open Source rzeczy w ogóle .

Dotyczyło to nawet różnych dystrybucji Linuksa. W systemie Linux - wcześniejszym niż FHS [1] - możesz mieć Pythona w / usr / bin / lub / usr / local / bin /. Lub może nie został zainstalowany, więc zbudowałeś swój własny i umieściłeś go w ~ / bin

Solaris był najgorszy, na jakim kiedykolwiek pracowałem, częściowo jako przejście z Berkeley Unix na System V. Możesz skończyć z rzeczami w / usr /, / usr / local /, / usr / ucb, / opt / itp. To może sprawić, że dla niektórych naprawdę długich ścieżek. Mam wspomnienia z Sunfreeware.com instalującego każdy pakiet we własnym katalogu, ale nie mogę sobie przypomnieć, czy to dowiązało pliki binarne do / usr / bin, czy nie.

Aha, a czasami / usr / bin był na serwerze NFS [2].

Więc envnarzędzie zostało opracowane, aby obejść ten problem.

Potem można było pisać #!/bin/env interpreteri tak długo, jak ścieżka była właściwa, istniała rozsądna szansa na bieg. Oczywiście rozsądne oznaczenie (dla Pythona i Perla), że ustawiłeś także odpowiednie zmienne środowiskowe. W przypadku bash / ksh / zsh to po prostu działało.

Było to ważne, ponieważ ludzie przekazywali skrypty powłoki (takie jak Perl i Python), a jeśli na stałe zapisałeś / usr / bin / python na stacji roboczej Red Hat Linux, zepsuje się na SGI ... no cóż, nie , Myślę, że IRIX umieścił pytona we właściwym miejscu. Ale na stacji Sparc może wcale nie działać.

Tęsknię za moją stacją Sparc. Ale nie za dużo. Ok, teraz masz mnie trollującego po E-Bay. Bastages

[1] Standard hierarchii systemu plików. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Tak, a czasem ludzie nadal robią takie rzeczy. I nie, nie miałem na pasku ani rzepy, ani cebuli.


5

Jeśli uruchamiasz skrypt w środowisku wirtualnym, powiedzmy venv, wykonanie go which pythonpodczas pracy venvwyświetli ścieżkę do interpretera Pythona:

~/Envs/venv/bin/python

Zauważ, że nazwa środowiska wirtualnego jest osadzona w ścieżce do interpretera Pythona. Dlatego też zakodowanie tej ścieżki w skrypcie spowoduje dwa problemy:

  • Jeśli prześlesz skrypt do repozytorium, zmusisz innych użytkowników do posiadania tej samej nazwy środowiska wirtualnego . Dzieje się tak, jeśli najpierw zidentyfikują problem.
  • Nie będzie można uruchomić skryptu w wielu środowiskach wirtualnych, nawet jeśli wszystkie wymagane pakiety były dostępne w innych środowiskach wirtualnych.

Dlatego, aby dodać odpowiedź Jonathana , idealnym shebang jest #!/usr/bin/env pythonnie tylko przenośność w różnych systemach operacyjnych, ale także przenośność w środowiskach wirtualnych!


3

Biorąc pod uwagę problemy związane z przenośnością między python2i python3, zawsze należy określić jedną z wersji, chyba że program jest zgodny z obydwoma.

Niektóre dystrybucje pythonwysyłane są python3przez jakiś czas z linkami - nie polegaj na pythonbyciu python2.

Podkreśla to PEP 394 :

Aby tolerować różnice między platformami, cały nowy kod, który musi wywoływać interpreter Pythona, nie powinien określać Pythona, a raczej python2 lub python3 (lub bardziej szczegółowe wersje python2.x i python3.x; zobacz uwagi dotyczące migracji ) . Tego rozróżnienia należy dokonać w shebangach, podczas wywoływania ze skryptu powłoki, podczas wywoływania za pomocą wywołania system () lub przy każdym innym kontekście.


2

Mówi interpreterowi, z którą wersją Pythona uruchomić program, gdy masz wiele wersji Pythona.


0

Pozwala wybrać plik wykonywalny, którego chcesz użyć; co jest bardzo przydatne, jeśli być może masz wiele instalacji Pythona i różne moduły w każdym i chcesz wybrać. na przykład

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())

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.