Почему люди пишут скрипт #!/Usr/bin/env python в первой строке скрипта Python?
Мне кажется, что файлы работают без этой строки.
Ответы
Ответ 1
Если у вас установлено несколько версий Python, /usr/bin/env
гарантирует, что используемый интерпретатор является первым в вашей среде $PATH
. Альтернативой могло бы быть hardcode что-то вроде #!/usr/bin/python
; это нормально, но менее гибко.
В Unix исполняемый файл, который должен интерпретироваться, может указывать, какой интерпретатор должен использовать, имея #!
в начале первой строки, за которым следует интерпретатор (и любые флаги, которые могут понадобиться).
Если вы говорите о других платформах, конечно, это правило не применяется (но эта строка "shebang" не наносит вреда и поможет, если вы когда-нибудь скопируете этот script на платформу с базой Unix, таких как Linux, Mac и т.д.).
Ответ 2
Это называется строка shebang. Поскольку запись Wikipedia объясняет:
В вычислениях shebang (также называемый hashbang, hashpling, pound bang или crunchbang) относится к символам "#!" когда они являются первыми двумя символами в директиве интерпретатора в качестве первой строки текстового файла. В Unix-подобной операционной системе загрузчик программ принимает эти два символа в качестве признака того, что файл является script, и пытается выполнить этот script с помощью интерпретатора, указанного в остальной части первой строки в файл.
См. также Запись в Unix FAQ.
Даже в Windows, где строка shebang не определяет интерпретатор, который должен быть запущен, вы можете передать параметры интерпретатору, указав их на линии shebang. Я считаю полезным хранить общую строку shebang в одноразовых сценариях (например, те, которые я пишу, когда отвечаю на вопросы о SO), поэтому я могу быстро протестировать их как на Windows, так и на ArchLinux.
утилита env позволяет вам вызывать команду на пути:
Первый оставшийся аргумент указывает имя программы для вызова; он выполняется в соответствии с переменной среды PATH
. Любые оставшиеся аргументы передаются в качестве аргументов этой программы.
Ответ 3
Развернувшись немного на других ответах, вот небольшой пример того, как ваши сценарии командной строки могут попасть в проблему путем неосторожного использования строк /usr/bin/env
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
Модуль json не существует в Python 2.5.
Один из способов защитить эту проблему - использовать имена команд с версией python, которые обычно устанавливаются с большинством Pythons:
$ 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")
Ответ 4
Чтобы запустить python script, нам нужно сказать оболочке три вещи:
- Что файл script
- Какой интерпретатор мы хотим выполнить script
- Путь указанного интерпретатора
Шебанг #!
выполняет (1.). Шебанг начинается с #
, потому что символ #
является маркером комментариев на многих языках сценариев. Поэтому содержимое строки shebang автоматически игнорируется интерпретатором.
Команда 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.)
Ответ 5
Возможно, ваш вопрос в этом смысле:
Если вы хотите использовать: $python myscript.py
Вам не нужна эта строка вообще. Система вызовет python, а затем интерпретатор python запустит ваш script.
Но если вы намерены использовать: $./myscript.py
Вызывая его напрямую, как обычная программа, или bash script, вам нужно написать эту строку, чтобы указать системе, для которой программа используется для ее запуска (а также сделать ее исполняемой с помощью chmod 755
)
Ответ 6
Технически, в Python это просто строка комментариев.
Эта строка используется, только если вы запустите py script из оболочки (из командной строки). Это известно как "Shebang!" и используется в различных ситуациях, а не только с помощью скриптов Python.
Здесь он инструктирует оболочку запускать определенную версию Python (чтобы позаботиться о остальной части файла.
Ответ 7
Основная причина заключается в том, чтобы сделать перенос script переносимым по средам операционной системы.
Например, в 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
Однако все эти различия могут сделать перенос script переносимым на всех, используя:
#!/usr/bin/env python
Ответ 8
Системный вызов 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
). Давайте подтвердим это, прочитав 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 указывает на шебанги: 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
было бы возможным, но главная проблема в том, что все ломается, если мы когда-нибудь решим перевести команду на другой язык.
Шебангс прекрасно решает эту проблему.
Ответ 9
Возможно, имеет смысл подчеркнуть одну вещь, которую большинство пропустили, что может помешать немедленному пониманию. Когда вы вводите python
в терминале, вы обычно не предоставляете полный путь. Вместо этого исполняемый файл просматривается в переменной среды PATH
. В свою очередь, если вы хотите напрямую выполнить программу Python, /path/to/app.py
, нужно сказать оболочке, какой интерпретатор использовать (через хэш-бэнг, что другие участники объясняют выше).
Hashbang ожидает полный путь к интерпретатору. Таким образом, чтобы запустить вашу программу Python напрямую, вам необходимо предоставить полный путь к двоичному файлу Python, который значительно отличается, особенно с учетом использования virtualenv. Для решения проблемы переносимости используется трюк с /usr/bin/env
. Последнее изначально предназначено для изменения среды на месте и выполнения команды в ней. Когда никаких изменений не предусмотрено, она запускает команду в текущей среде, что эффективно приводит к тому же поиску PATH
, который делает трюк.
Источник из unix stackexchange
Ответ 10
Это соглашение оболочки, которое сообщает оболочке, какая программа может выполнить script.
#!/usr/bin/env python
разрешает путь к двоичному файлу Python.
Ответ 11
Рекомендуемый способ, предложенный в документации:
2.2.2. Исполняемые скрипты Python
В системах BSDish Unix скрипты Python могут быть сделаны напрямую исполняемый файл, подобно сценариям оболочки, путем размещения строки
#! /usr/bin/env python3.2
из http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts
Ответ 12
Вы можете попробовать эту проблему, используя 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
Ответ 13
Мне кажется, что файлы работают без этой строки.
Если да, то, возможно, вы запускаете программу Python в Windows? Windows не использует эту строку - вместо этого используется расширение имени файла для запуска программы, связанной с расширением файла.
Однако в 2011 году была разработана "Python launcher" , которая (в некоторой степени) имитирует этот Linux поведение для Windows. Это ограничивается только выбором того, какой интерпретатор Python запускается - например. для выбора между Python 2 и Python 3 в системе, где оба установлены. Пусковая установка может быть установлена как py.exe
с помощью установки Python и может быть связана с файлами .py
, чтобы запускающая программа проверила эту строку и, в свою очередь, запустила указанную версию интерпретатора Python.
Ответ 14
Он просто указывает, какой интерпретатор вы хотите использовать. Чтобы понять это, создайте файл через терминал, выполнив touch test.py
, затем введите в этот файл следующее:
#!/usr/bin/env python3
print "test"
и выполните chmod +x test.py
, чтобы выполнить ваш script исполняемый файл. После этого, когда вы делаете ./test.py
, вы должны получить сообщение об ошибке:
File "./test.py", line 2
print "test"
^
SyntaxError: Missing parentheses in call to 'print'
потому что python3 не поддерживает оператор печати.
Теперь перейдите и измените первую строку своего кода на:
#!/usr/bin/env python2
и он будет работать, печатая test
в stdout, потому что python2 поддерживает оператор печати. Итак, теперь вы узнали, как переключаться между script интерпретаторами.
Ответ 15
Это означает больше исторической информации, чем "реального" ответа.
Помните, что в те дни у вас было МНОЖЕСТВО Unix-подобных операционных систем, дизайнеры которых имели собственное представление о том, куда помещать вещи, а иногда вообще не включали Python, Perl, Bash или множество других GNU/Open Source вещей.,
Это было даже верно для разных дистрибутивов Linux. В Linux - pre-FHS [1] -you может содержать 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] Да, и иногда люди все еще делают подобные вещи. И нет, я не носил на поясе ни репы, ни лука.
Ответ 16
Если вы используете script в виртуальной среде, скажем venv
, тогда выполнение which python
при работе над venv
будет отображать путь к интерпретатору Python:
~/Envs/venv/bin/python
Обратите внимание, что имя виртуальной среды встроено в путь к интерпретатору Python. Следовательно, hardcoding этот путь в вашем script вызовет две проблемы:
- Если вы загружаете script в репозиторий, вы заставляете других пользователей иметь одно и то же имя виртуальной среды. Это если они сначала идентифицируют проблему.
- Вы не сможете запускать script в нескольких виртуальных средах, даже если у вас есть все необходимые пакеты в других виртуальных средах.
Поэтому, чтобы добавить к Jonathan ответ, идеальный shebang #!/usr/bin/env python
, а не только для переносимости между ОС, но для переносимость в виртуальных средах!
Ответ 17
Учитывая проблемы переносимости между python2
и python3
, вы всегда должны указывать любую версию, если ваша программа не совместима с обоими.
Некоторые дистрибутивы в течение некоторого времени отправляют python
с символикой python3
- не полагайтесь на python
на python2
.
Это подчеркивается PEP 394:
Чтобы переносить различия между платформами, весь новый код, который необходимо вызвать интерпретатор Python, не следует указывать python, но скорее следует указать либо python2, либо python3 (или более конкретный python2.x и python3.x; см. Заметки о переносе). Эта различие должно быть сделано в shebangs, при вызове из оболочки script, при вызове через вызов system() или при вызове в любом в другом контексте.
Ответ 18
Он сообщает интерпретатору, какая версия python запускает программу, когда у вас есть несколько версий python.
Ответ 19
Это позволяет вам выбрать исполняемый файл, который вы хотите использовать; что очень
удобно, если у вас есть несколько установок 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" "[email protected]"
elif [ -x $ALTERNATIVE_PYTHON ]; then
echo Using alternative python $ALTERNATIVE_PYTHON
exec $ALTERNATIVE_PYTHON "$0" "[email protected]"
else
echo Using fallback python $FALLBACK_PYTHON
exec python3 "$0" "[email protected]"
fi
exit 127
'''
__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())
Ответ 20
это сообщает script, где находится каталог python!
#! /usr/bin/env python