Почему люди пишут скрипт #!/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