Когда сценарий оболочки начинается с #!
этой первой строки, это комментарий в отношении оболочки. Однако первые два символа имеют значение для другой части системы: ядра. Два персонажа #!
называются шебанг . Чтобы понять роль шебанга, вам нужно понять, как выполняется программа.
Выполнение программы из файла требует действий от ядра. Это делается как часть execve
системного вызова. Ядро должно проверить права доступа к файлу, освободить ресурсы (память и т. Д.), Связанные с исполняемым файлом, который в данный момент выполняется в вызывающем процессе, выделить ресурсы для нового исполняемого файла и передать управление новой программе (и другим вещам, которые Я не буду упоминать). execve
Системный вызов заменяет код процесса в настоящее время работает; есть отдельный системный вызов fork
для создания нового процесса.
Для этого ядро должно поддерживать формат исполняемого файла. Этот файл должен содержать машинный код, организованный так, чтобы его понимало ядро. Сценарий оболочки не содержит машинный код, поэтому он не может быть выполнен таким образом.
Механизм shebang позволяет ядру отложить задачу интерпретации кода в другой программе. Когда ядро видит, что исполняемый файл начинается с #!
, оно читает следующие несколько символов и интерпретирует первую строку файла (без начального #!
и необязательного пробела) как путь к другому файлу (плюс аргументы, которые я не буду здесь обсуждать) ). Когда ядру говорят выполнить файл /my/script
, и он видит, что файл начинается со строки #!/some/interpreter
, ядро выполняется /some/interpreter
с аргументом /my/script
. Затем /some/interpreter
нужно решить, что /my/script
это файл сценария, который он должен выполнить.
Что если файл не содержит нативный код в формате, понятном ядру, и не начинается с шебанга? Что ж, тогда файл не является исполняемым, и execve
системный вызов завершается ошибкой с кодом ошибки ENOEXEC
(ошибка формата исполняемого файла).
Это может быть концом истории, но большинство оболочек реализуют запасную функцию. Если ядро возвращается ENOEXEC
, оболочка просматривает содержимое файла и проверяет, выглядит ли он как сценарий оболочки. Если оболочка считает, что файл выглядит как сценарий оболочки, она выполняет его сама. Детали того, как это происходит, зависят от оболочки. Вы можете увидеть кое-что из того, что происходит, добавив ps $$
в свой скрипт, и многое другое, наблюдая за процессом, strace -p1234 -f -eprocess
где 1234 - это PID оболочки.
В bash этот резервный механизм реализован путем вызова, fork
но не execve
. Дочерний процесс bash самостоятельно очищает свое внутреннее состояние и открывает новый файл сценария для его запуска. Поэтому процесс, выполняющий сценарий, все еще использует исходное изображение кода bash и исходные аргументы командной строки, переданные при первоначальном вызове bash. ATT ksh ведет себя так же.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash, напротив, реагирует на это ENOEXEC
, вызывая /bin/sh
путь к сценарию, переданному в качестве аргумента. Другими словами, когда вы выполняете сценарий без shebang из dash, он ведет себя так, как будто сценарий имеет строку shebang #!/bin/sh
. Мкш и зш ведут себя одинаково.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh