Коли сценарій оболонки починається з #!
цього першого рядка, це коментар, що стосується оболонки. Однак перші два символи мають значення для іншої частини системи: ядра. Два персонажа #!
називають шебангом . Щоб зрозуміти роль шебангу, потрібно зрозуміти, як виконується програма.
Виконання програми з файлу вимагає дії з ядром. Це робиться як частина 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 оболонки.
В основному, цей механізм резервного копіювання реалізується за допомогою виклику, 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
із передачею в якості аргументу шлях до сценарію. Іншими словами, коли ви виконуєте сценарій shebangless з тире, він поводиться так, як ніби у сценарію була лінія shebang #!/bin/sh
. Mksh і zsh поводяться однаково.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh