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

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

Ответы [ 19 ]

995 голосов
/ 12 марта 2010

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

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

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

246 голосов
/ 12 марта 2010

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

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

См. Также запись Unix FAQ .

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

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

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

145 голосов
/ 12 марта 2010

Немного расширив остальные ответы, вот небольшой пример того, как ваши скрипты командной строки могут попасть в неприятности при неосторожном использовании /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")
82 голосов
/ 26 марта 2012

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

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

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

Команда env выполняет (2.) и (3.). Процитирую "grawity"

Обычное использование команды env - запуск интерпретаторов путем использование факта, что env будет искать $ PATH для команды запускать. Поскольку линия Шебанга требует абсолютного пути указан, и так как расположение различных переводчиков (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.)

40 голосов
/ 17 января 2015

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

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

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

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

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

39 голосов
/ 12 марта 2010

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

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

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

36 голосов
/ 21 июля 2011

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

Например, в 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
33 голосов

Системный вызов 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 указывает на шебанги: 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

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

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

21 голосов
/ 18 марта 2014

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

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

Источник из unix stackexchange

12 голосов
/ 11 декабря 2011

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

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

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

#! /usr/bin/env python3.2

из http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts

...