Почему люди пишут #!/ Usr/bin/env pybang в первой строке скрипта Python?
Мне кажется, что файлы без этой строки работают одинаково.
22 ответа
Если у вас установлено несколько версий Python, /usr/bin/env
убедитесь, что используемый переводчик является первым в вашей среде $PATH
, Альтернативой было бы жестко закодировать что-то вроде #!/usr/bin/python
; это нормально, но менее гибко.
В Unix исполняемый файл, который должен интерпретироваться, может указывать, какой интерпретатор использовать, имея #!
в начале первой строки, за которой следует интерпретатор (и любые флаги, которые могут ему понадобиться).
Если вы говорите о других платформах, конечно, это правило не применяется (но эта "линия Шебанга" не причиняет вреда и поможет, если вы когда-нибудь скопируете этот скрипт на платформу с базой Unix, такую как Linux, Mac, так далее).
Это называется линией Шебанга. Как объясняется запись в Википедии:
В вычислениях шебанг (также называемый хэш-бэнг, хэшинг, фунт-бэнг или хруст-бэнг) относится к символам "#!" когда они являются первыми двумя символами в директиве интерпретатора в качестве первой строки текстового файла. В Unix-подобной операционной системе загрузчик программы воспринимает присутствие этих двух символов как указание на то, что файл является сценарием, и пытается выполнить этот сценарий, используя интерпретатор, указанный в оставшейся части первой строки файла.
Смотрите также запись FAQ по Unix.
Даже в Windows, где строка shebang не определяет интерпретатор для запуска, вы можете передать опции интерпретатору, указав их в строке shebang. Я считаю полезным сохранять общую строку shebang в одноразовых сценариях (например, в тех, которые я пишу, отвечая на вопросы по SO), чтобы я мог быстро протестировать их как в Windows, так и в ArchLinux.
Утилита env позволяет вам вызывать команду на пути:
Первый оставшийся аргумент указывает имя программы для вызова; ищется в соответствии с
PATH
переменная окружения. Все оставшиеся аргументы передаются в качестве аргументов этой программе.
Развернем немного другие ответы, вот небольшой пример того, как ваши сценарии командной строки могут попасть в неприятности из-за неосторожного использования /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")
Чтобы запустить скрипт на python, нам нужно сказать оболочке три вещи:
- Что файл является скриптом
- Какой интерпретатор мы хотим выполнить сценарий
- Путь указанного переводчика
Шебанг #!
выполняет (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.)
exec
системный вызов ядра Linux понимает шебанги ( #!
) изначально
Когда вы делаете на 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
). Это читает файл ELF, правильно помещает его в память и запускает новый процесс с ним. Смотрите также: Как ядро получает исполняемый двоичный файл, работающий под Linux?
Наконец, вы можете добавить свои собственные обработчики shebang с помощью binfmt_misc
механизм. Например, вы можете добавить собственный обработчик для .jar
файлы Этот механизм даже поддерживает обработчики по расширению файла. Другое приложение - для прозрачного запуска исполняемых файлов другой архитектуры с QEMU.
Я не думаю, что POSIX определяет shebangs, однако: /questions/17972824/razbit-yunit-testyi-na-drugoj-proekt/17972828#17972828, хотя он упоминается в разделах обоснования и в форме "если исполняемые сценарии поддерживаются системой, то что-то может случиться".
Возможно, ваш вопрос в этом смысле:
Если вы хотите использовать: $python myscript.py
Вам не нужна эта линия вообще. Система вызовет python, а затем интерпретатор python запустит ваш скрипт.
Но если вы собираетесь использовать: $./myscript.py
Вызывая его напрямую, как обычную программу или скрипт bash, вам нужно написать эту строку, чтобы указать системе, какую программу использовать для ее запуска (и также сделать ее исполняемой с помощью chmod 755
)
Основная причина сделать это - сделать скрипт переносимым между средами операционной системы.
Например, в 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
Технически, в Python это просто строка комментария.
Эта строка используется, только если вы запускаете скрипт py из оболочки (из командной строки). Это известно как "Шебанг!" и используется в различных ситуациях, а не только в сценариях Python.
Здесь он указывает оболочке запустить определенную версию Python (чтобы позаботиться об остальной части файла.
Вероятно, имеет смысл подчеркнуть одну вещь, которую большинство пропустило, что может помешать немедленному пониманию. Когда вы печатаете python
в терминале вы обычно не предоставляете полный путь. Вместо этого исполняемый файл просматривается в PATH
переменная окружения. В свою очередь, когда вы хотите выполнить программу Python напрямую, /path/to/app.py
необходимо указать оболочке, какой интерпретатор использовать (через hashbang, что другие авторы объясняют выше).
Hashbang ожидает полного пути к переводчику. Таким образом, чтобы запустить вашу программу Python напрямую, вы должны указать полный путь к двоичному файлу Python, который значительно варьируется, особенно с учетом использования virtualenv. Для решения переносимости трюк с /usr/bin/env
используется. Последний изначально предназначен для изменения среды на месте и запуска в ней команды. Когда никаких изменений не предусмотрено, команда запускается в текущей среде, что в результате приводит к тому же PATH
поиск, который делает трюк.
Это соглашение оболочки, которое сообщает оболочке, какая программа может выполнить скрипт.
#! / usr / bin / env python
разрешает путь к двоичному файлу Python.
Рекомендуемый способ, предложенный в документации:
2.2.2. Исполняемые скрипты Python
В системах BSD Unish, скрипты Python можно сделать непосредственно исполняемыми, например скрипты оболочки, поместив строку
#! /usr/bin/env python3.2
Он просто указывает, какой интерпретатор вы хотите использовать. Чтобы понять это, создайте файл через терминал, выполнив 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 поддерживает оператор печати. Итак, теперь вы узнали, как переключаться между интерпретаторами сценариев.
Вы можете попробовать эту проблему, используя 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
Мне кажется, что файлы без этой строки работают одинаково.
Если так, то, возможно, вы используете программу Python в Windows? Windows не использует эту строку - вместо этого она использует расширение имени файла для запуска программы, связанной с расширением файла.
Однако в 2011 году был разработан "модуль запуска Python", который (в некоторой степени) имитирует такое поведение Linux для Windows. Это ограничивается только выбором, какой интерпретатор Python запускается - например, для выбора между Python 2 и Python 3 в системе, где установлены оба. Пусковая установка опционально устанавливается как py.exe
путем установки Python, и может быть связан с .py
файлы, чтобы программа запуска проверила эту строку и, в свою очередь, запустила указанную версию интерпретатора Python.
Это означает больше исторической информации, чем "реального" ответа.
Помните, что в те дни у вас было МНОЖЕСТВО 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] Да, и иногда люди все еще делают подобные вещи. И нет, я не носил на поясе ни репы, ни лука.
Если вы запускаете свой скрипт в виртуальной среде, скажем venv
затем выполняет which python
работая над venv
отобразит путь к интерпретатору Python:
~/Envs/venv/bin/python
Обратите внимание, что имя виртуальной среды встроено в путь к интерпретатору Python. Поэтому жесткое кодирование этого пути в вашем скрипте вызовет две проблемы:
- Если вы загружаете сценарий в репозиторий, вы заставляете других пользователей иметь такое же имя виртуальной среды. Это если они сначала идентифицируют проблему.
- Вы не сможете запустить сценарий в нескольких виртуальных средах, даже если у вас были все необходимые пакеты в других виртуальных средах.
Поэтому, чтобы добавить к ответу Jonathan Cline IEEE, идеальный шебанг #!/usr/bin/env python
не только для переносимости между операционными системами, но и для переносимости через виртуальные среды!
Линия #!/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
Он сообщает интерпретатору, с какой версией python запускать программу, если у вас несколько версий python.
Учитывая проблемы переносимости между python2
а также python3
, вы всегда должны указывать любую версию, если ваша программа не совместима с обеими.
Некоторые дистрибутивы поставляются python
символическая ссылка на python3
какое-то время - не надейтесь python
являющийся python2
,
Это подчеркивается ПКП 394:
Чтобы допустить различия между платформами, весь новый код, который должен вызывать интерпретатор Python, не должен указывать python, а должен указывать либо python2, либо python3 (или более конкретные версии python2.x и python3.x; см. Примечания по миграции)., Это различие должно быть сделано в шебангах, при вызове из сценария оболочки, при вызове через вызов system() или при вызове в любом другом контексте.
Это позволяет вам выбрать исполняемый файл, который вы хотите использовать; что очень удобно, если, возможно, у вас есть несколько установок 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())
Когда вы выполняете файл python, вы можете использовать
./file.py
где файл - это имя файла. /usr/bin/env — это ПУТЬ, тогда python — это python 2, а python3 — это python 3 (да)
#!/usr/bin/env python
также может разрешить выполнение файла python другими программами, если вы используете
chmod +x file.py
.
Это говорит сценарию, где находится каталог python!
#! /usr/bin/env python