Wenn ich dieses Skript ausführe, das ausgeführt werden soll, bis es getötet wird ...
# foo.sh
while true; do sleep 1; done
... Ich kann es nicht finden mit ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... aber wenn ich nur den allgemeinen " #!
" Header zum Skript hinzufüge ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... dann wird das Skript mit dem gleichen ps
Befehl auffindbar ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Warum ist das so?
Dies könnte eine verwandte Frage sein: Ich dachte, " #
" sei nur ein Kommentarpräfix, und wenn ja, " #! /usr/bin/bash
" ist es selbst nichts weiter als ein Kommentar. Aber " #!
" hat es eine größere Bedeutung als nur einen Kommentar?
shell-script
shell
ps
shebang
StoneThrow
quelle
quelle
Antworten:
Wenn die aktuelle interaktive Shell ausgeführt wird
bash
und Sie ein Skript ohne#!
-linebash
ausführen , wird das Skript ausgeführt. Der Prozess wird in derps ax
Ausgabe als nur angezeigtbash
.In einem anderen Terminal:
Verbunden:
Die relevanten Abschnitte bilden das
bash
Handbuch:Dies bedeutet, dass das Ausführen
./foo.sh
in der Befehlszeile, wennfoo.sh
keine#!
-line vorhanden ist, dasselbe ist wie das Ausführen der Befehle in der Datei in einer Subshell, dh alsMit einer richtigen
#!
-Linie auf zB/bin/bash
ist es soquelle
ps
das Skript als "/usr/bin/bash ./foo.sh
" ausgeführt wird. Im ersten Fall wird also, wie Sie sagen, Bash das Skript ausführen, aber müsste dieses Skript nicht, wie im zweiten Fall, an die ausführbare Bash-Verzweigungsdatei "übergeben" werden? (und wenn ja, ich stelle mir vor, es wäre mit der Pipe zu grep auffindbar ...?)$$
immer noch auf den alten in der Unterschale (echo $BASHPID
/bash -c 'echo $PPID'
) verweist .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). Derexecve
Systemaufruf ersetzt den Code des aktuell ausgeführten Prozesses. Es gibt einen separaten Systemaufruffork
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 FehlercodeENOEXEC
(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 Sieps $$
in Ihrem Skript etwas hinzufügen , und mehr, indem Sie den Prozess beobachten, beistrace -p1234 -f -eprocess
dem 1234 die PID der Shell ist.In Bash wird dieser Fallback-Mechanismus durch Aufrufen implementiert,
fork
jedoch nicht durch Aufrufenexecve
. 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.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.quelle
bash
untergeordnetes Element, da es gegabelt ist, Zugriff auf dasselbeargv[]
Array wie sein übergeordnetes Element hat, wodurch es "die ursprünglichen Befehlszeilenargumente kennt, die beim ursprünglichen Aufrufen von bash übergeben wurden" und if Aus diesem Grund wird dem Kind das ursprüngliche Skript nicht als explizites Argument übergeben (daher kann es vom grep nicht gefunden werden). Ist das korrekt?BINFMT_SCRIPT
Modul steuert dies und kann entfernt / modularisiert werden, obwohl es normalerweise statisch mit dem Kernel verbunden ist), aber ich verstehe nicht, warum Sie es jemals wollen würden, außer vielleicht in einem eingebetteten System . Als Workaround für diese Möglichkeitbash
hat man ein Konfigurationsflag (HAVE_HASH_BANG_EXEC
) zum ausgleichen!ps
als Befehlszeilenargumente ausgegeben wird, aber nur bis zu einem gewissen Punkt: Es muss einen vorhandenen Speicherpuffer ändern, es kann diesen Puffer nicht vergrößern. Wenn Bash versucht,argv
den Namen des Skripts zu ändern , funktioniert dies nicht immer. Dem untergeordneten Element wird kein Argument übergeben, da das untergeordnete Element keinenexecve
Systemaufruf enthält. Es ist genau dasselbe Bash-Prozess-Image, das weiterhin ausgeführt wird.Im ersten Fall wird das Skript von einem abgespaltenen Kind aus Ihrer aktuellen Shell ausgeführt.
Sie sollten zuerst
echo $$
eine Shell ausführen und sich diese dann ansehen, deren übergeordnete Prozess-ID die Prozess-ID Ihrer Shell ist.quelle