#! / Bin / sh читается переводчиком?

66

В bashили sh, я предполагаю , что все , что начинается с #это комментарий .

Но в bashскриптах мы пишем:

#!/bin/bash

А в скриптах Python есть:

#!/bin/python

Означает ли это, что #сам по себе является комментарием, а #!нет?

Гаурав Шарма
источник
1
И как только вы начнете просматривать профили Apparmor, вы увидите #include. Там тоже #не подразумевается как комментарий.
4
@ vasa1 Но ключевым моментом, который часто не учитывается в строках hashbang в начале сценариев оболочки, является то, что они являются комментариями .
Элия ​​Каган

Ответы:

100

#!Линия используется , прежде чем скрипт запускается, то игнорируется при выполнении сценария.

Вы спрашиваете, в чем разница между строкой Шебанга и обычным комментарием.

Строка, начинающаяся с #!такого же комментария, как и любая другая строка, начинающаяся с #. Это верно, если #!первая строка файла или где-либо еще. #!/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, и расширение может определять, какие сценарии выполняются. (Это не означает, что вам нужно давать расширения для скриптов, но это одна из причин, почему, если вы это сделаете, они должны быть правильными.)

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

Линия Shebang для сценариев Bash

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

Линия Шебанга для скриптов Python

Вы также сказали (в первой версии вашего вопроса, перед редактированием), что запускаете свои скрипты Python #!/bin/sh read by the interpretor. Если вы имеете это в виду буквально, то вам определенно следует прекратить это делать. Если hello.pyначинается с этой строки, выполняется ./hello.pyrun:

/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

Это принимает пользовательский ввод в интерактивном режиме, выводит его обратно пользователю построчно и добавляет его в конец файла «script».

Полезно? Не очень. Концептуально интересно? Полностью! Да. (В некотором роде.)

Концептуально похожие концепции программирования / написания сценариев (просто для удовольствия)

Элия ​​Каган
источник
@ Rinzwind Thx! (Между прочим, этот ответ не возникает в другом месте, если вам это интересно.)
Элия ​​Каган,
@Rinzwind Не волнуйтесь, с 8 ответами за 1 час, вероятно, будет гораздо больше :-)
Guntbert
1
Если он всегда игнорируется, то что делает -xфлаг Pythons ?
Gerrit
4
@gerrit Хороший вопрос. На любом языке, где интерпретатор / компилятор сообщает о сообщениях с номерами строк, содержимое комментариев игнорируется, но строки комментариев по-прежнему учитываются . Добавление комментария или пустой строки перед строкой кода по-прежнему приводит к увеличению номера этой строки кода. -x«скип [s] первая строка ...» 2 - я линия получает пронумерованные 1вместо 2, 3 - я линия , 2а не 3, и т.д. Вот почему вы не должны использовать этот флаг. ;) -xпредназначен для написания сценариев в не-Unix-подобных ОС, которые имеют шебангоподобный синтаксис, не начинающийся с #(таким образом, не комментарий Python).
Элия ​​Каган
4
В Perl, если интерпретатор запускается непосредственно ( perl script.plпротив ./script.pl) , то интерпретатор будет читать строку притона для синтаксического анализа флагов , таких как -w. Однако не рекомендуется полагаться на эту функцию.
Стоп Harm Моника
7

Шебанг - это последовательность символов, состоящая из знака числа символов и восклицательного знака (например, «#!»), Когда она встречается в качестве начальных двух символов в начальной строке скрипта.

В операционных системах * nix, когда запускается скрипт, начинающийся с shebang, загрузчик программы анализирует остальную часть начальной строки скрипта как директиву интерпретатора; вместо этого запускается указанная программа-интерпретатор, передавая ей в качестве аргумента путь, который изначально использовался при попытке запустить скрипт. Например, если сценарий назван с путем «path / to / your-script», и он начинается со следующей строки:

#!/bin/sh

затем загрузчик программы получает указание запустить программу "/ bin / sh", например, например, оболочку Bourne или совместимую оболочку, передавая "путь / к / вашему скрипту" в качестве первого аргумента.

Соответственно, скрипт называется с путем «путь / к / python-скрипт» и начинается со следующей строки:

#!/bin/python

Затем загруженная программа получает указание запустить программу «/ bin / python», например, интерпретатор Python, передавая «путь / to / python-script» в качестве первого аргумента.

Короче "#" закомментирует строку, а последовательность символов "#!" встречается как первые два символа в начальной строке скрипта, как указано выше.

Подробнее см. Почему некоторые скрипты начинаются с #! ...?

Источник: некоторые разделы этого ответа получены (с небольшими изменениями) из Шебанга (Unix) в английской Википедии ( авторами Википедии ). Эта статья лицензирована в соответствии с CC-BY-SA 3.0 , так же, как пользовательский контент здесь, на AU, поэтому этот вывод разрешен с указанием авторства.

Горан Мишкович
источник
4

#!вызывается, shebangкогда он встречается в качестве начальных двух символов в начальной строке скрипта. Он используется в скриптах для указания интерпретатора для выполнения. Это shebangдля операционной системы (ядра), а не для оболочки; поэтому он не будет интерпретироваться как комментарий.

Предоставлено: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

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

Подробная информация: http://wiki.bash-hackers.org/scripting/basics#the_shebang

saji89
источник
0

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

Наконец, вы можете добавить свои собственные обработчики shebang с binfmt_miscмеханизмом. Например, вы можете добавить пользовательский обработчик для .jarфайлов . Этот механизм даже поддерживает обработчики по расширению файла. Другое приложение - для прозрачного запуска исполняемых файлов другой архитектуры с QEMU .

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

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
источник