Почему люди пишут #! / Usr / bin / env pybang в первой строке скрипта Python?


1049

Мне кажется, что файлы без этой строки работают одинаково.


1
Ответ ниже утверждает, что это просто строка комментария. Это не всегда так. У меня есть «Привет, мир!» CGI-скрипт (.py), который будет запускаться и отображать веб-страницу #!/usr/bin/env pythonтолько вверху.
Чакотай


Они могут бежать, но не в намеченной среде
Николас Гамильтон

18
Я посещал этот пост так много раз за 7 лет, потому что иногда забывал о hashbang env.
BugHunterUK

Ответы:


1085

Если у вас установлено несколько версий Python, /usr/bin/envубедитесь, что используемый интерпретатор является первым в вашей среде $PATH. Альтернативой было бы жестко закодировать что-то вроде#!/usr/bin/python ; это нормально, но менее гибко.

В Unix исполняемый файл, предназначенный для интерпретации, может указывать, какой интерпретатор использовать, имея #!в начале первой строки, за которым следует интерпретатор (и любые флаги, которые могут ему понадобиться).

Если вы говорите о других платформах, конечно, это правило не применяется (но эта «линия Шебанга» не причиняет вреда и поможет, если вы когда-нибудь скопируете этот скрипт на платформу с базой Unix, такую ​​как Linux, Mac , так далее).


267
Просто добавить: это применимо, когда вы запускаете его в Unix, делая его исполняемым ( chmod +x myscript.py), а затем запускаете его напрямую: ./myscript.pyвместо простого python myscript.py.
Крейг МакКуин

28
Использование envдает максимальную гибкость в том, что пользователь может выбрать интерпретатор для использования путем изменения PATH. Часто эта гибкость не требуется, хотя и недостатком является то, что Linux, например, не может использовать имя сценария для имени процесса psи возвращается к «python». При упаковке приложений Python для дистрибутивов, например, я бы советовал не использовать env.
pixelbeat

9
pyLauncher может использовать строку Shebang на Windows. Он включен в Python 3.3 или может быть установлен независимо .
JFS

6
Важное предупреждение, возвращаемое значение env в конце концов истекает. Что вряд ли повлияет на вас, если вы запускаете недолговечные процессы. Тем не менее, у меня были процессы, умирающие с сообщением /usr/bin/env: Key has expiredчерез много часов.
Малавердиер

4
@malaverdiere можете ли вы ссылаться на какие-либо ресурсы, которые объясняют это поведение истечения срока действия? Я не могу их найти.
Майкл

267

Это называется линией Шебанга . Как объясняется запись в Википедии :

В вычислениях шебанг (также называемый хэш-бэнг, хэшплинг, фунт-бэнг или хруст-бэнг) относится к символам "#!" когда они являются первыми двумя символами в директиве интерпретатора в качестве первой строки текстового файла. В Unix-подобной операционной системе загрузчик программы воспринимает присутствие этих двух символов как указание на то, что файл является сценарием, и пытается выполнить этот сценарий, используя интерпретатор, указанный в оставшейся части первой строки файла.

Смотрите также запись FAQ по Unix .

Даже в Windows, где строка shebang не определяет интерпретатор для запуска, вы можете передать опции интерпретатору, указав их в строке shebang. Я считаю полезным хранить общую строку shebang в одноразовых скриптах (например, тех, которые я пишу, отвечая на вопросы по SO), чтобы я мог быстро протестировать их как в Windows, так и в ArchLinux .

Утилита окр позволяет выполнить команду на пути:

Первый оставшийся аргумент указывает имя программы для вызова; ищется по PATHпеременной окружения. Все оставшиеся аргументы передаются в качестве аргументов этой программе.


30
Легко найти с помощью Google - если кто-то знает ключевые слова («линия Шебанга» имеет важное значение).
Арафангион

14
На самом деле, это объяснение яснее, чем другие ссылки, которые я проверял в Google. Всегда лучше получить 1 параграф объяснения, нацеленного на вопрос, а не читать полное руководство по каждому потенциальному использованию.
Сэм Голдберг

1
@Arafangion, вы, вероятно, найдете этот вопрос полезным. TL; DR: symbolhound.com
ulidtko

@ulidtko: Интересная поисковая система, подумайте над тем, чтобы написать ответ, чтобы вопрос Джона Гарсиаса был лучше.
Арафангион

1
«Даже в Windows, где строка shebang не определяет интерпретатор для запуска, вы можете передать опции интерпретатору, указав их в строке shebang». Это просто ложь; если такое случается, это потому, что сам интерпретатор обрабатывает строку shebang. Если интерпретатор не имеет особого распознавания линий Шебанга, то такого не происходит. Windows ничего не делает со строками Шебанга . «В этом случае вы можете описать пусковую установку python: python.org/dev/peps/pep-0397 .
Kaz

154

Немного расширив остальные ответы, приведу небольшой пример того, как ваши скрипты командной строки могут попасть в неприятности из-за неосторожного использования /usr/bin/envстрок Шебанга:

$ /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

Модуль json не существует в Python 2.5.

Один из способов защиты от подобных проблем - использовать версионные имена команд python, которые обычно устанавливаются с большинством питонов:

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

Если вам просто нужно различать Python 2.x и Python 3.x, последние выпуски Python 3 также предоставляют python3имя:

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

27
Хм, это не то, что я получил от этого поста.
Гленн Джекман

1
Разница между локальным и глобальным. Если which pythonвозвращается /usr/bin/python, локальный путь к каталогу может быть жесток: #!/usr/bin/python. Но это менее гибко, чем #!/usr/bin/env pythonглобальное применение.
noobninja

85

Чтобы запустить скрипт на python, нам нужно сказать оболочке три вещи:

  1. Что файл является скриптом
  2. Какой интерпретатор мы хотим выполнить сценарий
  3. Путь указанного переводчика

Шебанг #!выполняет (1.). Шебанг начинается с символа, #потому что #символ является маркером комментария во многих языках сценариев. Поэтому содержимое строки Шебанга автоматически игнорируется интерпретатором.

Команда envвыполняет (2.) и (3.). Чтобы процитировать "Гравитация"

Обычное использование envкоманды - запуск интерпретаторов, используя тот факт, что env будет искать в $ PATH команду, которую ей говорят запустить. Поскольку строка shebang требует указания абсолютного пути, а расположение различных интерпретаторов (perl, bash, python) может сильно различаться, обычно используется:

#!/usr/bin/env perl  вместо того, чтобы пытаться угадать, является ли это / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl или / home / MrDaniel / usr / bin / perl в системе пользователя ...

С другой стороны, env почти всегда находится в / usr / bin / env. (За исключением случаев, когда это не так; некоторые системы могут использовать / bin / env, но это довольно редкий случай и происходит только в системах, отличных от Linux.)


1
"Гравитация" где?
Pacerier

44

Возможно, ваш вопрос в этом смысле:

Если вы хотите использовать: $python myscript.py

Вам не нужна эта линия вообще. Система вызовет python, а затем интерпретатор python запустит ваш скрипт.

Но если вы собираетесь использовать: $./myscript.py

Вызывая его напрямую, как обычную программу или скрипт bash, вам нужно написать эту строку, чтобы указать системе, какую программу использовать для ее запуска, (а также сделать ее исполняемой с chmod 755 )


или вы можете написать python3 myscript.py
Виджай Шенкер

44

execСистемный вызов в Linux ядро понимает shebangs ( #!) изначально

Когда вы делаете на Bash:

./something

в Linux это вызывает execсистемный вызов с путем ./something.

Эта строка ядра вызывается для файла, переданного по адресуexec : https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25.

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

Он читает самые первые байты файла и сравнивает их с #! .

Если сравнение истинно, то остальная часть строки анализируется ядром Linux, которое делает еще один execвызов с путем /usr/bin/env pythonи текущим файлом в качестве первого аргумента:

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

и это работает для любого языка сценариев, который использует # в качестве символа комментария.

И да, вы можете сделать бесконечный цикл с:

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

Bash распознает ошибку:

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

#! бывает, что он читается человеком, но это не обязательно.

Если файл начинается с разных байтов, то execсистемный вызов будет использовать другой обработчик. Другой самый важный встроенный обработчик для исполняемых файлов ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, который проверяет байты 7f 45 4c 46(которые также являются человеческими читаемый для .ELF). Давайте подтвердим это, прочитав 4 первых байта /bin/ls, который является исполняемым файлом ELF:

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

вывод:

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

Поэтому, когда ядро ​​видит эти байты, оно берет файл ELF, правильно помещает его в память и запускает новый процесс с ним. Смотрите также: Как ядро ​​получает исполняемый двоичный файл, работающий под Linux?

Наконец, вы можете добавить свои собственные обработчики shebang с binfmt_miscмеханизмом. Например, вы можете добавить пользовательский обработчик для .jarфайлов . Этот механизм даже поддерживает обработчики по расширению файла. Другое приложение - для прозрачного запуска исполняемых файлов другой архитектуры с QEMU .

Я не думаю, что POSIX определяет shebangs, однако: https://unix.stackexchange.com/a/346214/32558 , хотя он упоминается в разделах обоснования и в форме «если исполняемые сценарии поддерживаются системой, что-то может случиться». macOS и FreeBSD также, похоже, реализуют это.

PATH поисковая мотивация

Вероятно, одной большой мотивацией для существования шебангов является тот факт, что в Linux мы часто хотим запускать команды так PATHже, как:

basename-of-command

вместо:

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

Но тогда, без механизма shebang, как Linux узнает, как запускать файлы каждого типа?

Жесткое кодирование расширения в командах:

 basename-of-command.py

или осуществляя поиск PATH на каждом интерпретаторе:

python basename-of-command

было бы возможно, но это главная проблема, что все ломается, если мы когда-нибудь решим реорганизовать команду на другой язык.

Шебангс прекрасно решает эту проблему.


39

Технически, в Python это просто строка комментария.

Эта строка используется только если вы запускаете скрипт py из оболочки (из командной строки). Это известно как " Шебанг !" и используется в различных ситуациях, а не только в сценариях Python.

Здесь он указывает оболочке запустить определенную версию Python (чтобы позаботиться об остальной части файла.


Шебанг - это концепция Unix. Стоит упомянуть, что он работает и в Windows, если вы установили пусковую установку Python py.exe . Это часть стандартной установки Python.
Флорисла

38

Основная причина сделать это - сделать скрипт переносимым между средами операционной системы.

Например, в mingw скрипты на python используют:

#!/c/python3k/python 

и в дистрибутиве GNU / Linux это либо:

#!/usr/local/bin/python 

или

#!/usr/bin/python

и под самой лучшей коммерческой системой Unix SW / HW из всех (OS / X) это:

#!/Applications/MacPython 2.5/python

или на FreeBSD:

#!/usr/local/bin/python

Однако все эти различия могут сделать сценарий переносимым через все:

#!/usr/bin/env python

2
Под MacOSX это тоже так /usr/bin/python. Под Linux Python, установленный системой, также почти наверняка /usr/bin/python(я больше ничего не видел, и это не имело бы никакого смысла). Обратите внимание, что могут быть системы, которые не имеют /usr/bin/env.
Альберт

1
Если вы используете OSX и используете Homebrew и следуете инструкциям по умолчанию, он будет находиться под #! / Usr / local / bin / python
Will

@ Jean-PaulCalderone: см. Ответ Саадж ниже.
pydsigner

Обновление на 2018 год: Bare pythonне так переносим, ​​это дистрибутив Python по умолчанию. Arch Linux по умолчанию установлен на Python 3 долгое время, и, возможно, дистрибутивы тоже думают об этом, потому что Python 2 поддерживается только до 2020 года.
mati865

22

Вероятно, имеет смысл подчеркнуть одну вещь, которую большинство пропустило, что может помешать немедленному пониманию. Когда вы печатаете pythonв терминале, вы обычно не указываете полный путь. Вместо этого исполняемый файл ищется в PATHпеременной окружения. В свою очередь, когда вы хотите выполнить программу на Python напрямую, /path/to/app.pyнужно указать оболочке, какой интерпретатор использовать (через hashbang , что другие авторы объясняют выше).

Hashbang ожидает полного пути к переводчику. Таким образом, чтобы запустить вашу программу на Python напрямую, вы должны указать полный путь к двоичному файлу Python, который значительно варьируется, особенно с учетом использования virtualenv . Для решения переносимости используется трюк с /usr/bin/env. Последний изначально предназначен для изменения среды на месте и запуска в ней команды. Если никаких изменений не предусмотрено, команда запускается в текущей среде, что фактически приводит к тому жеPATH поиску, что и уловка.

Исходник из unix stackexchange


14

Это соглашение оболочки, которое сообщает оболочке, какая программа может выполнить скрипт.

#! / usr / bin / env python

разрешает путь к двоичному файлу Python.


12

Рекомендуемый способ, предложенный в документации:

2.2.2. Исполняемые скрипты Python

В системах BSD Unish, скрипты Python можно сделать непосредственно исполняемыми, например скрипты оболочки, поставив строку

#! /usr/bin/env python3.2

от http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts


9

Вы можете попробовать эту проблему, используя virtualenv

Вот test.py

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

Создавать виртуальные среды

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

активируйте каждую среду, затем проверьте различия

echo $PATH
./test.py

9

Он просто указывает, какой интерпретатор вы хотите использовать. Чтобы понять это, создайте файл через терминал, выполнив touch test.py, затем введите в этот файл следующее:

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

и сделать, chmod +x test.pyчтобы сделать ваш скрипт исполняемым. После этого, когда вы делаете, ./test.pyвы должны получить сообщение об ошибке:

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

потому что python3 не поддерживает оператор печати.

Теперь перейдите и измените первую строку вашего кода на:

#!/usr/bin/env python2

и он будет работать, печатая testна стандартный вывод, потому что python2 поддерживает оператор печати. Итак, теперь вы узнали, как переключаться между интерпретаторами сценариев.


9

Мне кажется, что файлы без этой строки работают одинаково.

Если так, то, возможно, вы используете программу Python в Windows? Windows не использует эту строку - вместо этого она использует расширение имени файла для запуска программы, связанной с расширением файла.

Однако в 2011 году был разработан «модуль запуска Python», который (в некоторой степени) имитирует такое поведение Linux для Windows. Это ограничивается только выбором запускаемого интерпретатора Python - например, для выбора между Python 2 и Python 3 в системе, где установлены оба. Модуль запуска по выбору устанавливается как py.exeпри установке Python и может быть связан с .pyфайлами, так что модуль запуска проверяет эту строку и, в свою очередь, запускает указанную версию интерпретатора Python.


6
Он также может использовать $ python myscript.py.
Синан Юнюр

Я сделал ошибку, не имея строки, и использовал python script.py, и однажды я просто сделал ./myscript.py, и все перестало работать, а затем понял, что система ищет файл как сценарий оболочки вместо сценария python.
Гуагуа

8

Это означает больше исторической информации, чем «реального» ответа.

Помните , что еще в день вы имели МНОГО UNIX - подобные операционные системам , в которых дизайнеры все имел свое собственное представление о том, где положить вещи, а иногда и не включает в себя Python, Perl, Bash, или много другого GNU / Open Source материала на все ,

Это было даже верно для разных дистрибутивов Linux. В Linux - pre-FHS [1] - у вас может быть python в / usr / bin / или / usr / local / bin /. Или, возможно, он не был установлен, поэтому вы создали свой собственный и поместили его в ~ / bin

Solaris был худшим из всех, над которыми я когда-либо работал, частично как переход с Berkeley Unix на System V. Вы можете оказаться с вещами в / usr /, / usr / local /, / usr / ucb, / opt / и т.д. Это может сделать для некоторых действительно длинных путей. У меня есть воспоминания о том, как Sunfreeware.com устанавливал каждый пакет в свой собственный каталог, но я не могу вспомнить, содержит ли он символические ссылки в / usr / bin или нет.

О, и иногда / usr / bin находился на NFS-сервере [2].

Так что envутилита была разработана, чтобы обойти это.

Тогда вы могли бы написать, #!/bin/env interpreterи, пока путь был верным, у вещей был разумный шанс бежать. Конечно, разумно (для Python и Perl) означало, что вы также установили соответствующие переменные среды. Для bash / ksh / zsh это просто сработало.

Это было важно, потому что люди передавали скрипты оболочки (такие как perl и python), и если вы жестко запрограммировали / usr / bin / python на вашей рабочей станции Red Hat Linux, то это будет плохо работать на SGI ... ну, нет Я думаю, что IRIX поставил Python в нужное место. Но на станции Sparc он может вообще не работать.

Я скучаю по своей станции sparc. Но не много. Хорошо, теперь ты заставляешь меня троллить по E-Bay. Bastages.

[1] Стандарт иерархии файловой системы. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Да, и иногда люди все еще делают подобные вещи. И нет, я не носил на поясе ни репы, ни лука.


5

Например, если вы выполняете свой сценарий в виртуальной среде, venvто выполнение which pythonво время работы venvпокажет путь к интерпретатору Python:

~/Envs/venv/bin/python

Обратите внимание, что имя виртуальной среды встроено в путь к интерпретатору Python. Поэтому жесткое кодирование этого пути в вашем скрипте вызовет две проблемы:

  • Если вы загружаете скрипт в репозиторий, вы заставляете других пользователей иметь такое же имя виртуальной среды . Это если они сначала идентифицируют проблему.
  • Вы не сможете запустить скрипт в нескольких виртуальных средах, даже если у вас были все необходимые пакеты в других виртуальных средах.

Поэтому, чтобы добавить к ответу Джонатана , идеальный шебанг#!/usr/bin/env python не только переносимость между операционными системами, но и переносимость между виртуальными средами!


3

Учитывая проблемы переносимости между python2иpython3 , вы всегда должны указывать любую версию, если ваша программа не совместима с обеими.

Некоторые дистрибутивы поставляются pythonслинкованы в python3то время как сейчас - не полагаться на pythonсуществеpython2 .

Это подчеркивается ПКП 394 :

Чтобы допустить различия между платформами, весь новый код, который должен вызывать интерпретатор Python, не должен указывать python, а должен указывать либо python2, либо python3 (или более конкретные версии python2.x и python3.x; см. Примечания по миграции ). , Это различие следует делать в шебангах, при вызове из сценария оболочки, при вызове через вызов system () или при вызове в любом другом контексте.


2

Он сообщает интерпретатору, с какой версией python запускать программу, если у вас несколько версий python.


0

Это позволяет вам выбрать исполняемый файл, который вы хотите использовать; что очень удобно, если, возможно, у вас есть несколько установок Python и разные модули в каждом и вы хотите выбрать. например

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

0

Строка #!/bin/bash/python3или #!/bin/bash/pythonуказывает, какой компилятор Python использовать. У вас может быть установлено несколько версий Python. Например,
a.py:

#!/bin/bash/python3
print("Hello World")

это скрипт на python3, а
b.py:

#!/bin/bash/python
print "Hello World"

является скриптом Python 2.x
Чтобы запустить ./a.pyили ./b.pyиспользовать этот файл , вам необходимо дать привилегии выполнения файлов заранее, иначе выполнение приведет к Permission deniedошибке.
Для предоставления разрешения на исполнение,

chmod +x a.py

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.