Чому люди записують #! / Usr / bin / env pybhon shebang у перший рядок сценарію Python?


1046

Мені здається, що файли працюють однаково без цього рядка.


1
У відповіді нижче зазначено, що це лише рядок коментарів. Це не завжди так. У мене є "Привіт, світ!" CGI-скрипт (.py), який буде запускатися та відображати веб-сторінку #!/usr/bin/env pythonвгорі.
Чакотай


Вони можуть бігати, але не в намічених умовах
Ніколас Гамільтон,

18
Я відвідував цю посаду стільки разів за 7 років, тому що інколи забуваю env hashbang. Скопіюйте макарони :)
BugHunterUK

Відповіді:


1082

Якщо у вас встановлено кілька версій 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.
піксельбіт

9
pyПускова програма може використовувати лінію shebang у Windows. Він включений в Python 3.3 або його можна встановити самостійно .
jfs

6
Важливе слово попередження, повернення вартості env з часом закінчується. Що навряд чи вплине на вас, якщо ви запускаєте недовговічні процеси. Однак у мене процеси, які вмирали з повідомленням, /usr/bin/env: Key has expiredчерез багато годин.
malaverdiere

4
@malaverdiere Ви можете зв’язатись із будь-якими ресурсами, що пояснюють цю поведінку, що закінчується? Я не можу їх знайти.
Майкл

266

Це називається лінією shebang . Як пояснює запис у Вікіпедії :

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

Дивіться також запис у FAQ про Unix .

Навіть у Windows, де лінія shebang не визначає запуск інтерпретатора, ви можете передати варіанти інтерпретатору, вказавши їх на лінії shebang. Мені здається корисним зберігати загальну лінію shebang в разових сценаріях (таких, як ті, про які я пишу, відповідаючи на запитання на SO), тому я можу швидко перевірити їх як у Windows, так і в ArchLinux .

Утиліта окр дозволяє виконати команду на шляху:

Перший аргумент, що залишився, вказує назву програми для виклику; його шукають відповідно до PATHзмінної середовища. Будь-які інші аргументи передаються як аргументи до цієї програми.


30
Легко знайти в Google - якщо ви знаєте ключові слова ("рядок shebang" є важливою).
Арафангіон

14
Насправді це пояснення чіткіше, ніж інші посилання, які я перевірив у Google. Завжди приємніше отримати пояснення з 1 пунктом, спрямоване на питання, а не читати цілий посібник, що стосується кожного потенційного використання.
Сем Голдберг

1
@Arafangion, ймовірно, це питання буде корисним. TL; DR: symbolhound.com
ulidtko

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

1
"Навіть у Windows, де лінія shebang не визначає запуск інтерпретатора, ви можете передавати варіанти інтерпретатору, вказавши їх на лінії shebang." Це просто помилково; якщо таке трапляється, це тому, що сам перекладач обробляє лінію шебанга. Якщо перекладач не має спеціального розпізнавання для рядків шебанга, то подібного не відбувається. Windows нічого не робить із лініями shebang. "Те, що ви, можливо, описуєте в цьому випадку, - це програма запуску python: python.org/dev/peps/pep-0397 .
Kaz,

154

Розгорнувши трохи інші відповіді, ось невеликий приклад того, як ваші сценарії командного рядка можуть потрапити в проблему шляхом обережного використання /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")

27
Гм, це не те, що я вийшов з цієї посади.
glenn jackman

1
Різниця між локальним та глобальним. Якщо which pythonповертається /usr/bin/python, локальний шлях до каталогу може бути жорстокий: #!/usr/bin/python. Але це менш гнучко, ніж те, #!/usr/bin/env pythonщо має глобальне застосування.
noobninja

85

Для того щоб запустити скрипт python, нам потрібно сказати оболонці про три речі:

  1. Що файл - це сценарій
  2. Який інтерпретатор ми хочемо виконати сценарієм
  3. Шлях зазначеного перекладача

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

У envкоманді виробляється (2) і (3). Цитувати "grawity"

Поширене використання 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
"grawity" де?
Pacerier

44

Можливо, ваше питання в цьому сенсі:

Якщо ви хочете використовувати: $python myscript.py

Ця лінія вам зовсім не потрібна. Система викличе python, а потім інтерпретатор python запустить ваш сценарій.

Але якщо ви маєте намір використовувати: $./myscript.py

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


або ви можете написати python3 myscript.py
vijay shanker

44

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

Коли ви займаєтеся басом:

./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?

Нарешті, ви можете додати власні обробники шебанг із 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

і за найкращою комерційною системою 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 / місцеві / бен / пітон!
Уілла

@ Жан-Поль Кальдерон: Дивіться відповідь Саая нижче.
пидсигнер

Оновлення на 2018 рік: Bare python- це не портативний портал, а інтерпретатор Python за замовчуванням. Arch Linux за замовчуванням на Python 3 тривалий час, і, можливо, дистрибутиви думають і про це, тому що Python 2 підтримується лише до 2020 року.
mati865

22

Напевно, має сенс підкреслити одне, що найбільше пропустили, що може завадити негайному зрозумінню. Під час введення pythonтерміналу ви зазвичай не надаєте повний шлях. Натомість виконуваний файл шукає PATHзмінну середовища. У свою чергу, коли ви хочете виконати програму Python безпосередньо, /path/to/app.pyпотрібно сказати оболонці, який інтерпретатор використовувати (через хешбанг , що пояснюють інші учасники).

Хашбанг очікує повного шляху до перекладача. Таким чином, щоб безпосередньо запустити вашу програму Python, ви повинні надати повний шлях до бінарних файлів Python, який значно відрізняється, особливо з урахуванням використання virtualenv . Для вирішення проблеми переносимості використовується хитрість /usr/bin/env. Остання спочатку призначена для зміни середовища на місці та виконання команди в ньому. Якщо ніяких змін не передбачено, він запускає команду в поточному середовищі, що ефективно призводить до того ж PATHпошуку, який виконує трюк.

Джерело з unix stackexchange


14

Це умовна оболонка, яка повідомляє оболонці, яка програма може виконати сценарій.

#! / usr / bin / env python

вирішує шлях до бінарного файлу Python.


12

Рекомендований спосіб, запропонований у документації:

2.2.2. Виконані сценарії Python

У системах BSD'ish Unix сценарії Python можна зробити безпосередньо виконуваними, як сценарії оболонки, шляхом розміщення рядка

#! /usr/bin/env python3.2

від http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts


9

Ви можете спробувати це питання, використовуючи virtualenv

Ось тест.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до stdout, тому що python2 підтримує оператор друку. Отже, тепер ви дізналися, як переключатися між інтерпретаторами сценаріїв.


9

Мені здається, що файли працюють однаково без цього рядка.

Якщо так, то, можливо, ви запускаєте програму Python в Windows? Windows не використовує цей рядок - натомість використовує розширення імені файлу для запуску програми, пов'язаної з розширенням файлу.

Однак у 2011 році був розроблений "запуск Python", який (певною мірою) імітує цю поведінку Linux для Windows. Це обмежується лише вибором інтерпретатора Python, наприклад, для вибору між Python 2 та Python 3 у системі, де встановлено обидва. Запускається програма, необов'язково встановлюється як py.exeпри встановленні Python, і може бути пов’язана з .pyфайлами, щоб пускач перевірив цей рядок і в свою чергу запустив вказану версію інтерпретатора Python.


6
Він також може використовувати $ python myscript.py.
Sinan Ünür

Я допустив помилку, не маючи рядка і використав 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 / etc. Це могло зробити для деяких дійсно довгих шляхів. У мене є запам'ятовування матеріалів із 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 вона може взагалі не працювати.

Я сумую за своїм спарком. Але не багато. Гаразд, тепер у мене є тролінг по E-Bay. Бастажі.

[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.

На цьому наголошує PEP 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())

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.