Шебанг определяет оболочку, которая запускает скрипт?

84

Это может быть глупый вопрос, но я все же задаю его. Если бы я объявил Шебанг

#!/bin/bash 

в начале my_shell_script.sh, так что я всегда должен вызывать этот скрипт с помощью Bash

[my@comp]$bash my_shell_script.sh

или я могу использовать, например,

[my@comp]$sh my_shell_script.sh

а мой скрипт определяет работающую оболочку с помощью шебанга? То же самое происходит с kshоболочкой? Я использую AIX.

jrara
источник
6
с вашей стороны есть небольшая путаница: когда вы делаете "_some_shell some_script", он запускает _some_shell и просит его интерпретировать some_script. Так что нет, если вы выполните "sh my_shell_script.sh", он не будет интерпретировать shebang, но вместо этого будет интерпретировать скрипт в sh. Чтобы использовать шебанг: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Оливье Дюлак

Ответы:

117

Притон #! является читаемым человеком экземпляр магического числа , состоящего из байтов строки 0x23 0x21, которая используется в exec()семействе функций для определения , является ли файл , который будет выполняться сценарий или двоичным. Когда присутствует exec()shebang, вместо этого будет запускаться исполняемый файл, указанный после shebang.

Обратите внимание, что это означает, что если вы вызываете сценарий, указав интерпретатор в командной строке, как это делается в обоих случаях, приведенных в вопросе, exec()он выполнит интерпретатор, указанный в командной строке, он даже не будет смотреть на сценарий.

Итак, как уже отмечали другие, если вы хотите exec()вызвать интерпретатор, указанный в строке shebang, сценарий должен иметь установленный исполняемый бит и вызываться как ./my_shell_script.sh.

Поведение легко продемонстрировать с помощью следующего скрипта:

#!/bin/ksh
readlink /proc/$$/exe

Объяснение:

  • #!/bin/kshопределяет kshбыть переводчиком.

  • $$ содержит PID текущего процесса.

  • /proc/pid/exe является символической ссылкой на исполняемый файл процесса (по крайней мере, в Linux; в AIX /proc/$$/object/a.out - ссылка на исполняемый файл).

  • readlink выведет значение символической ссылки.

Пример:

Примечание : я показываю это на Ubuntu, где оболочка по умолчанию /bin/shявляется символической ссылкой на тир ИЭ /bin/dashи /bin/kshявляется символической ссылкой /etc/alternatives/ksh, которая , в свою очередь , является символической ссылкой на /bin/pdksh.

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash
Томас Найман
источник
спасибо Томасу за этот ответ. Представьте, что мы запускаем скрипт как дочерний процесс из Node.js, Java или чего-то еще. Можем ли мы запустить процесс "exec", а затем exec запустит скрипт оболочки? Я спрашиваю, потому что я ищу ответы на этот вопрос: stackoverflow.com/questions/41067872/…
Александр Миллс
1
@AlexanderMills exec()Упомянутый в этом ответе системный вызов, команда exec- встроенная оболочка, поэтому вы не можете вызвать exec программу из Node.js или Java. Однако любая команда оболочки, вызываемая, например, Runtime.exec()в Java, в конечном итоге обрабатывается exec()системным вызовом.
Томас Найман
Да, я знаком с API Java, который вы только что упомянули. Интересно, есть ли способ как-то вызвать вызов exec () более низкого уровня из Node.js
Александр Миллс
@AlexanderMills, я бы вообразил, child_process.{exec(),execFile(),spawn()} все будет реализовано с помощью C exec()(через process).
Томас Найман
10

Да. Кстати это не глупый вопрос. Ссылка на мой ответ здесь . Начиная скрипт с #!

  • Это называется шебанг или линия "взрыва".

  • Это не что иное, как абсолютный путь к интерпретатору Bash.

  • Он состоит из знака числа и символа восклицательного знака (#!), За которым следует полный путь к интерпретатору, например / bin / bash.

    Все сценарии в Linux выполняются с использованием интерпретатора, указанного в первой строке. Почти все сценарии bash часто начинаются с #! / Bin / bash (при условии, что Bash был установлен в / bin). Это гарантирует, что Bash будет использоваться для интерпретации сценария, даже если он выполняется под другой оболочкой. Шебанг был представлен Деннисом Ричи между версией 7 Unix и 8 в Bell Laboratories. Затем он был также добавлен к линии BSD в Беркли.

Игнорирование строки переводчика (шебанг)

Если вы не укажете строку интерпретатора, по умолчанию обычно используется / bin / sh. Но рекомендуется установить #! / Bin / bash line.

vfbsilva
источник
3
Чтобы уточнить, ядро ​​знает только, как выполнять статически связанные двоичные файлы и где найти информацию интерпретатора для других (специальное поле в двоичном файле или строка Шебанга). Обычно выполнение сценария оболочки означает следование линии shebang к оболочке, а затем следование за полем DT_INTERP в двоичном файле оболочки для динамического компоновщика.
Саймон Рихтер
5
Также обратите внимание, что это не ограничивается сценариями оболочки. Все текстовые файлы сценариев используют это. Например, #!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyдругая распространенная запись в shebang, используемая для поддержки нескольких систем, - это использование env для поиска интерпретатора, который вы хотите использовать, например#!/usr/bin/env perl #!/usr/bin/env python
sambler
@ sambler говоря о том env, что на самом деле должно быть предпочтительнее? Python и Perl часто используют env, в то время как в сценариях оболочки это часто опускается, и shebang указывает на рассматриваемую оболочку.
Полемон
1
@polemon меньше из которых предпочтительнее и больше, по которым пути меняются. Основные оболочки находятся на одном пути во всех системах. Современные версии Perl и Python могут быть установлены в разных местах на разных системах, поэтому использование env позволяет всегда работать одним и тем же шебангам, поэтому env используется больше для сценариев perl и python, чем для сценариев оболочки.
Sambler
envнайти программу в $ PATH - это что-то вроде хака. Он не устанавливает переменные среды, как следует из названия. $ PATH может быть разным результатом для разных пользователей. Но это помогает сценариям работать без изменений в системах, которые помещают разумный интерпретатор perl в какое-то странное место.
Джон Маховальд
4

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

Когда вы делаете на 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 新疆 改造 中心 法轮功 六四 事件
источник
1
Запуск ./somethingиз оболочки не передаст полный путь exec, но точно введенный путь. Вы можете исправить это в своем ответе? Сделайте echo "$0"в своем сценарии, и вы увидите, что это так.
AndiDog
2

Фактически, если вы берете это, следовательно, исполняемый файл, отмеченный в строке shebang, является просто исполняемым файлом. Имеет смысл использовать некоторый текстовый интерпретатор как исполняемый, но это не обязательно. Просто для пояснения и демонстрации я сделал довольно бесполезный тест:

#!/bin/cat
useless text
more useless text
still more useless text

Названный файл test.txt и установить exectuable бит chmod u+x test.txt, то «под названием» это: ./test.txt. Как и ожидалось, содержимое файла выводится. В этом случае кошка не игнорирует линию Шебанга. Он просто выводит все строки. Таким образом, любой полезный интерпретатор должен игнорировать эту строку Шебанга. Для bash, perl и PHP это просто строка комментария. Так что да, они игнорируют линию Шебанга.

Зигфрид
источник
-1

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

Короче говоря, не имеет значения, с какой оболочкой вы вызываете скрипт - ядро ​​в любом случае отправит выполнение соответствующему.

Владимир Парка
источник
4
Там есть заметная разница между bash ./myscript.shи ./myscript.sh.
CVN
Что вы подразумеваете под этой "заметной разницей"?
Джрара
3
@jrara Посмотрите мой ответ, утверждение, что «не имеет значения, с какой оболочкой вы вызываете скрипт», просто не соответствует действительности.
Томас Найман