Чи читається #! / Bin / sh перекладачем?


66

В bashабо sh, я припускаю , що все , що починається з #це коментар .

Але в bashсценаріях ми пишемо:

#!/bin/bash

А в сценаріях Python є:

#!/bin/python

Чи означає це, що #сам по собі є коментарем, тоді #!як ні?


1
І як тільки ви почнете переглядати профілі Apparmor, побачите #include. Там теж #це не означає як коментар.

4
@ vasa1 Але ключовим моментом, який часто не оцінюється щодо рядків хешбангу на початку скриптів оболонки, є те, що це коментарі .
Ілля Каган

Відповіді:


100

#!Лінія використовується , перш ніж скрипт запускається, то ігнорується при виконанні сценарію.

Ви запитуєте, в чому різниця між рядком shebang та звичайним коментарем.

Рядок, що починається з #!- це так само коментар, як і будь-який інший рядок, який починається з #. Це справедливо, якщо #!це перший рядок файлу чи деінде. #!/bin/sh має ефект , але його не читає сам перекладач .

#це не коментар у всіх мовах програмування, але, як ви знаєте, це коментар у оболонках у стилі Борна, включаючи shта bash(як і більшість оболонок у стилі Борна, як csh). Це також коментар у Python . І це коментар до різноманітних файлів конфігурації, які насправді взагалі не є сценаріями (як /etc/fstab).

Припустимо, сценарій оболонки починається з #!/bin/sh. Це коментар, і інтерпретатор (оболонка) ігнорує все в рядку після #символу.

Мета #!рядка - не давати інформацію перекладачеві. Мета #!рядка - повідомити операційній системі (або будь-який процес, який запускає інтерпретатор), що використовувати як інтерпретатор .

  • Якщо ви викликаєте скрипт як виконуваний файл, наприклад, запустивши ./script.sh, система звертається до першого рядка, щоб побачити, чи починається з нього #!, а потім нуль або більше пробілів, а потім команда. Якщо це так, він виконує цю команду з назвою скрипту як свого аргументу. У цьому прикладі він працює /bin/sh script.sh(або, технічно, /bin/sh ./script.sh).

  • Якщо ви викликаєте скрипт, чітко зателефонувавши до інтерпретатора, #!рядок ніколи не проводиться. Отже, якщо ви запустите sh script.sh, перший рядок не має ефекту. Якщо script2.shперший рядок #!/usr/games/nibbles, запущений sh script2.shне буде намагатися відкрити скрипт в nibbles(але ./script2.shбуде).

Ви помітите, що в жодному випадку розширення скрипта ( .sh), якщо воно є, не впливає на його виконання. У Unix-подібній системі це зазвичай не впливає на те, як виконується сценарій. У деяких інших системах, наприклад Windows, система #!shebang може повністю ігноруватися системою, а розширення може визначати, що виконує сценарії. (Це не означає, що вам потрібно давати розширення для сценаріїв, але це одна з причин, чому, якщо ви це зробите, вони повинні бути правильними.)

#!був обраний для того, щоб служити цій меті саме тому, що # починається коментар. #!Лінія для системи, а НЕ перекладач, і він повинен бути проігнорований інтерпретатором.

Лінія Шебанга для сценаріїв Баша

Ви (спочатку) сказали, що використовуєте #!/bin/shдля bashсценаріїв. Ви повинні зробити це лише в тому випадку, якщо сценарій не вимагає bashрозширень sh. shне завжди є посиланням на bash. Часто, в тому числі на всіх віддалених останніх системах Debian і Ubuntu , shє символьним посиланням на dash.

Лінія Шебанга для сценаріїв Python

Ви також сказали (у першій версії свого запитання, перш ніж редагувати), що запускаєте сценарії Python #!/bin/sh read by the interpretor. Якщо ви маєте на увазі це буквально, то вам обов'язково слід припинити це робити. Якщо hello.pyпочинається з цього рядка, запуск ./hello.pyвиконує:

/bin/sh read by the interpretor hello.py

/bin/shспробує виконати скрипт, який називається readby the interpretor hello.pyйого аргументами), read(сподіваємось) не знайдеться, і ваш сценарій Python ніколи не побачить інтерпретатора Python.

Якщо ви робите цю помилку, але не маєте проблеми, яку я описую, ви, ймовірно, посилаєтесь на ваші скрипти Python, чітко вказавши інтерпретатора (наприклад, python hello.py), викликаючи ігнорування першого рядка. Коли ви розповсюджуєте свої сценарії іншим або використовуєте їх довгий час пізніше, можливо, не буде зрозуміло, що це потрібно їм. Найкраще їх виправити зараз. Або принаймні повністю видаліть перший рядок, так що, коли вони не зможуть запустити ./повідомлення про помилку, матиме сенс.

Для сценаріїв Python, якщо ви знаєте, де перекладач Python знаходиться (або буде), ви можете записати #!рядок так само:

#!/usr/bin/python

Або, якщо це сценарій Python 3, вам слід вказати python3, оскільки pythonце майже завжди Python 2 :

#!/usr/bin/python3

Однак проблема полягає в тому, що, як /bin/shпередбачається, існує завжди і /bin/bashмайже завжди існує в системах, де bashпоставляється з ОС, Python може існувати в різних місцях.

Тому багато програмістів Python використовують це замість цього:

#!/usr/bin/env python

(Або #!/usr/bin/env python3для Python 3.)

Це змушує сценарій розраховувати на envте, щоб опинитися у "правильному місці", а не покладатися на pythonправильне місце. Це добре, адже:

  • envмайже завжди знаходиться в с /usr/bin.
  • У більшості систем залежно від того, хто python повинен запустити ваш сценарій, той, який з’являється першим у PATH. Починаючи hello.pyз #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, що (практично) еквівалентно бігу python hello.py.

Причина, яку ви не можете використовувати, #!pythonполягає в тому, що:

  • Ви хочете, щоб вказаний перекладач був заданий абсолютним шляхом (тобто, починаючи з /).
  • Процес виклику виконується python в поточному каталозі . Пошук шляху, коли команда не містить косу рису, є специфічною поведінкою оболонки.

Інколи Python або інший скрипт, який не є скриптом оболонки, матиме рядок shebang, починаючи з того, #!/bin/sh ...де ...є якийсь інший код. Іноді це правильно, оскільки є деякі способи викликати сумісну з Bourne оболонку ( sh) з аргументами, щоб змусити її викликати інтерпретатора Python. (Один із аргументів, мабуть, міститиме python.) Однак для більшості цілей #!/usr/bin/env pythonпростіше, елегантніше та швидше працювати так, як вам хочеться.

Лінії Шебанг іншими мовами

Багато мов програмування та сценаріїв, а також деякі інші формати файлів використовують #як коментар. Для будь-якого з них файл мовою може бути запущений програмою, яка сприймає його як аргумент, вказавши програму в першому рядку після #!.

У деяких мовах програмування #зазвичай це не коментар, але, як особливий випадок, перший рядок ігнорується, якщо він починається з #!. Це полегшує використання #!синтаксису, хоча #інше не робить коментар у рядку.

Рядки Shebang для файлів, які не працюють як сценарії

Хоча це менш інтуїтивно, будь-який файл, формат якого може вмістити перший рядок, починаючи з #!наступного повного шляху виконуваного файлу, може мати рядок shebang. Якщо ви це зробите, а файл позначений виконуваним, ви можете запустити його як програму ... змусивши його відкритись як документ.

Деякі програми використовують цю поведінку навмисно. Наприклад, у VMware .vmxфайли визначають віртуальні машини. Ви можете «запустити» віртуальну машину так, ніби це сценарій, тому що ці файли позначені виконуваним і мають рядок shebang, що призводить до їх відкриття в утиліті VMware.

Рядки Shebang для файлів, які не працюють як сценарії, але діють як сценарії в будь-якому випадку

rmвидаляє файли. Це не сценарна мова. Однак файл, який запускається #!/bin/rmі позначений виконуваним, можна запустити, а коли ви запустите його, rmвикликається на ньому, видаливши його.

Це часто розуміється як "файл видаляє себе". Але файл насправді не працює. Це більше схоже на ситуацію, описану вище для .vmxфайлів.

Тим не менше, оскільки #!рядок полегшує запуск простої команди (включаючи аргументи командного рядка), ви можете виконати деякі сценарії таким чином. Як простий приклад "сценарію", більш складного, ніж #!/bin/rm, розглянемо:

#!/usr/bin/env tee -a

Це вводить інтерактивне введення користувача, повторює його назад в користувач по черзі та додає його до кінця файлу "скрипт".

Корисно? Не надто. Концептуально цікавий? Повністю! Так. (Дещо.)

Концептуально подібні концепції програмування / сценарії (просто для розваги)


@Rinzwind Thx! (До речі, ця відповідь не походить деінде, якщо вам це цікаво.)
Елія Каган

@ Rinzwind Не хвилюйся, 8 відгуків через 1 годину, швидше за все, піднімуть :-)
guntbert

1
Якщо його завжди ігнорують, то що робить -xпрапор Пітонів ?
Герріт

4
@gerrit Добре запитання. Будь-якою мовою, де інтепретер / компілятор повідомляє повідомлення з номерами рядків, вміст коментарів ігнорується, але рядки коментарів все ще враховуються . Додавання коментаря або порожнього рядка до рядка коду все ще призводить до того, що цей рядок коду збільшується. -x"пропустити [s] перший рядок ..." 2-й рядок 1замість нумерується 2, 3-й рядок 2замість 3і т. д. Тому не слід використовувати цей прапор. ;) -xпризначений для сценаріїв на не-Unix-подібних ОС, у яких синтаксис, схожий на шебанг, не починається з #(таким чином, це не коментар Python).
Елія Каган

4
У Perl, якщо інтерпретатор запускається безпосередньо ( perl script.plпроти ./script.pl) , то інтерпретатор буде читати рядок кубла для синтаксичного аналізу прапорів , таких як -w. Однак не рекомендується покладатися на цю функцію.
OrangeDog

7

Шебанг - це послідовність символів, що складається з знака числа символів та знака оклику (наприклад, "#!"), Коли він зустрічається як два початкових символи в початковому рядку сценарію.

Під операційними системами * nix, коли виконується сценарій, починаючи з шебанга, програма завантажувача аналізує решту початкового рядка сценарію як директива інтерпретатора; задана програма інтерпретатора запускається замість цього, передаючи їй як аргумент шлях, який був спочатку використаний при спробі запуску сценарію. Наприклад, якщо сценарій названий зі шляху "шлях / до / ваш-скрипт", і він починається з наступного рядка:

#!/bin/sh

то завантажувач програми доручається запустити програму "/ bin / sh" замість цього, наприклад, оболонки Bourne або сумісної оболонки, передаючи "шлях / до / ваш-скрипт" в якості першого аргументу.

Відповідно, його сценарій іменується шляхом "шлях / до / python-script", і він починається з наступного рядка:

#!/bin/python

потім завантаженій програмі доручається запустити програму "/ bin / python" замість, наприклад, інтерпретатора Python, передаючи "шлях / до / python-script" в якості першого аргументу.

Якщо коротко, "#" прокоментує рядок, тоді як послідовність символів "#!" виникають як перші два символи в початковому рядку сценарію, мають значення, окреслені вище.

Докладніше див. Чому деякі сценарії починаються з #! ...?

Джерело: Деякі розділи цієї відповіді виведені (з незначною модифікацією) із Shebang (Unix) в англійській Вікіпедії (від авторів Wikipedia ). Ця стаття ліцензована під CC-BY-SA 3.0 , подібно до вмісту користувача тут в АС, тому таке виведення дозволено з атрибуцією.


4

#!називається shebangколи, коли це відбувається як два початкові символи в початковому рядку сценарію. Він використовується в скриптах для позначення перекладача для виконання. Значення shebangдля операційної системи (ядра), а не для оболонки; тому це не буде трактуватися як коментар.

Люб’язно: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

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

Детальна інформація: http://wiki.bash-hackers.org/scripting/basics#the_shebang


0

Ні, він використовується лише при execсистемному виклику ядра Linux і трактується як коментар інтерпретатора

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

./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, правильно вводить його в пам'ять і починає новий процес з ним. Дивіться також: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Нарешті, ви можете додати власні обробники шебанг із binfmt_miscмеханізмом. Наприклад, ви можете додати спеціальний обробник .jarфайлів . Цей механізм навіть підтримує обробники за допомогою розширення файлу. Інша програма полягає у прозорому запуску виконуваних файлів іншої архітектури за допомогою QEMU .

Я не думаю, що POSIX не вказує shebangs: https://unix.stackexchange.com/a/346214/32558 , хоча це вказується в розділах обґрунтування, а також у формі "якщо виконувані сценарії підтримуються системою, щось може трапляються ».

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