Wenn ein Shell-Skript mit beginnt #!
, ist diese erste Zeile ein Kommentar für die Shell. Die ersten beiden Zeichen sind jedoch für einen anderen Teil des Systems von Bedeutung: den Kernel. Die beiden Charaktere #!
werden Shebang genannt . Um die Rolle des Shebang zu verstehen, müssen Sie verstehen, wie ein Programm ausgeführt wird.
Das Ausführen eines Programms aus einer Datei erfordert eine Aktion des Kernels. Dies erfolgt im Rahmen des execve
Systemaufrufs. Der Kernel muss die Dateiberechtigungen überprüfen, die Ressourcen (Speicher usw.) freigeben, die der aktuell im aufrufenden Prozess ausgeführten ausführbaren Datei zugeordnet sind, Ressourcen für die neue ausführbare Datei zuweisen und die Steuerung auf das neue Programm übertragen (und vieles mehr) Ich werde nicht erwähnen). Der execve
Systemaufruf ersetzt den Code des aktuell ausgeführten Prozesses. Es gibt einen separaten Systemaufruf fork
zum Erstellen eines neuen Prozesses.
Dazu muss der Kernel das Format der ausführbaren Datei unterstützen. Diese Datei muss Maschinencode enthalten, der so organisiert ist, dass der Kernel ihn versteht. Ein Shell-Skript enthält keinen Maschinencode und kann daher nicht auf diese Weise ausgeführt werden.
Der Shebang-Mechanismus ermöglicht es dem Kernel, die Interpretation des Codes in ein anderes Programm zu verschieben. Wenn der Kernel erkennt, dass die ausführbare Datei mit beginnt #!
, liest er die nächsten Zeichen und interpretiert die erste Zeile der Datei (abzüglich des führenden #!
und optionalen Leerzeichens) als Pfad zu einer anderen Datei (plus Argumente, auf die ich hier nicht näher eingehen werde) ). Wenn der Kernel aufgefordert wird, die Datei auszuführen /my/script
und feststellt, dass die Datei mit der Zeile beginnt #!/some/interpreter
, wird der Kernel /some/interpreter
mit dem Argument ausgeführt /my/script
. Es liegt dann /some/interpreter
an Ihnen zu entscheiden, /my/script
dass es sich um eine Skriptdatei handelt, die ausgeführt werden soll.
Was ist, wenn eine Datei weder nativen Code in einem Format enthält, das der Kernel versteht, noch mit einem Shebang beginnt? Nun, dann ist die Datei nicht ausführbar und der execve
Systemaufruf schlägt mit dem Fehlercode ENOEXEC
(Fehler im ausführbaren Format) fehl .
Dies könnte das Ende der Geschichte sein, aber die meisten Shells implementieren eine Fallback-Funktion. Wenn der Kernel zurückkehrt ENOEXEC
, überprüft die Shell den Inhalt der Datei und ob sie wie ein Shell-Skript aussieht. Wenn die Shell denkt, dass die Datei wie ein Shell-Skript aussieht, führt sie es selbst aus. Die Details dazu hängen von der Shell ab. Sie können sehen, was passiert, indem Sie ps $$
in Ihrem Skript etwas hinzufügen , und mehr, indem Sie den Prozess beobachten, bei strace -p1234 -f -eprocess
dem 1234 die PID der Shell ist.
In Bash wird dieser Fallback-Mechanismus durch Aufrufen implementiert, fork
jedoch nicht durch Aufrufen execve
. Der untergeordnete Bash-Prozess löscht seinen internen Status von selbst und öffnet die neue Skriptdatei, um sie auszuführen. Daher verwendet der Prozess, der das Skript ausführt, weiterhin das ursprüngliche Bash-Codebild und die ursprünglichen Befehlszeilenargumente, die beim ursprünglichen Aufrufen von Bash übergeben wurden. ATT ksh verhält sich genauso.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Im Gegensatz dazu reagiert Dash, ENOEXEC
indem /bin/sh
der Pfad zum Skript als Argument übergeben wird. Mit anderen Worten, wenn Sie ein Shebangless-Skript von Dash aus ausführen, verhält es sich so, als hätte das Skript eine Shebang-Zeile mit #!/bin/sh
. Mksh und zsh verhalten sich genauso.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh