Kiedy zaczyna się skrypt powłoki #!
, ten pierwszy wiersz jest komentarzem w odniesieniu do powłoki. Jednak pierwsze dwa znaki mają znaczenie dla innej części systemu: jądra. Dwie postacie #!
nazywane są shebang . Aby zrozumieć rolę shebang, musisz zrozumieć, jak program jest wykonywany.
Wykonywanie programu z pliku wymaga działania z jądra. Odbywa się to w ramach execve
wywołania systemowego. Jądro musi zweryfikować uprawnienia do pliku, zwolnić zasoby (pamięć itp.) Związane z plikiem wykonywalnym aktualnie uruchomionym w procesie wywoływania, przydzielić zasoby dla nowego pliku wykonywalnego i przenieść kontrolę do nowego programu (i więcej rzeczy, które Nie wspomnę). execve
Wywołanie systemowe zastępuje kod aktualnie uruchomionego procesu; istnieje osobne wywołanie systemowe, fork
aby utworzyć nowy proces.
Aby to zrobić, jądro musi obsługiwać format pliku wykonywalnego. Ten plik musi zawierać kod maszynowy zorganizowany w sposób zrozumiały dla jądra. Skrypt powłoki nie zawiera kodu maszynowego, więc nie można go wykonać w ten sposób.
Mechanizm shebang pozwala jądru na odroczenie zadania interpretacji kodu do innego programu. Kiedy jądro widzi, że zaczyna się plik wykonywalny #!
, odczytuje kilka kolejnych znaków i interpretuje pierwszy wiersz pliku (minus #!
spację wiodącą i opcjonalną) jako ścieżkę do innego pliku (plus argumenty, których nie będę tutaj omawiać ). Kiedy jądro ma wykonać plik /my/script
i widzi, że plik zaczyna się od linii #!/some/interpreter
, jądro wykonuje się /some/interpreter
z argumentem /my/script
. Następnie /some/interpreter
należy zdecydować, że /my/script
jest to plik skryptu, który powinien wykonać.
Co jeśli plik nie zawiera natywnego kodu w formacie zrozumiałym dla jądra i nie zaczyna się od shebang? Cóż, wtedy plik nie jest wykonywalny, a execve
wywołanie systemowe kończy się niepowodzeniem z kodem błędu ENOEXEC
(błąd formatu wykonywalnego).
To może być koniec historii, ale większość pocisków implementuje funkcję awaryjną. Jeśli jądro powróci ENOEXEC
, powłoka sprawdza zawartość pliku i sprawdza, czy wygląda jak skrypt powłoki. Jeśli powłoka uważa, że plik wygląda jak skrypt powłoki, wykonuje go samodzielnie. Szczegóły tego, jak to robi, zależą od powłoki. Możesz zobaczyć, co się dzieje, dodając ps $$
do skryptu, a więcej, obserwując proces, w strace -p1234 -f -eprocess
którym 1234 to PID powłoki.
W bash ten mechanizm rezerwowy jest realizowany przez wywołanie, fork
ale nie execve
. Proces potomnego bash sam usuwa stan wewnętrzny i otwiera nowy plik skryptu, aby go uruchomić. Dlatego proces, który uruchamia skrypt, nadal używa oryginalnego obrazu kodu bash, a oryginalne argumenty wiersza poleceń przekazane przy pierwszym wywołaniu bash. ATT ksh zachowuje się w ten sam sposób.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash natomiast reaguje na ENOEXEC
wywołanie /bin/sh
ze ścieżką do skryptu przekazaną jako argument. Innymi słowy, wykonanie skryptu bez kreski z kreski zachowuje się tak, jakby skrypt zawierał linię shebang #!/bin/sh
. Mksh i zsh zachowują się w ten sam sposób.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh