Wird #! / Bin / sh vom Interpreter gelesen?

66

In bashoder sh, denke ich, ist alles, was mit beginnt, #ein Kommentar .

Aber in bashSkripten schreiben wir:

#!/bin/bash

Und in Python-Skripten gibt es:

#!/bin/python

Bedeutet dies, dass #für sich genommen ein Kommentar ist, während dies #!nicht der Fall ist?

Gaurav Sharma
quelle
1
Sobald Sie sich die Apparmor-Profile ansehen, werden Sie sehen #include. Auch hier ist das #nicht als Kommentar gemeint.
4
@ vasa1 Der entscheidende Punkt, der bei Hashbang-Zeilen am Anfang von Shell-Skripten häufig nicht berücksichtigt wird, ist, dass es sich um Kommentare handelt .
Eliah Kagan

Antworten:

100

Die #!Linie verwendet wird , bevor das Skript ausgeführt wird, dann ignoriert , wenn das Skript ausgeführt wird .

Sie fragen, was der Unterschied zwischen einer Shebang-Zeile und einem normalen Kommentar ist.

Eine Zeile, die mit beginnt, #!ist genauso ein Kommentar wie jede andere Zeile, die mit beginnt #. Dies ist der Fall, wenn dies die #!erste Zeile der Datei ist oder an einer anderen Stelle. #!/bin/sh hat einen Effekt , wird aber nicht vom Interpreter selbst gelesen .

#ist kein Kommentar in allen Programmiersprachen, aber, wie Sie wissen, ein Kommentar in Shells im Bourne-Stil, einschließlich shund bash(sowie in den meisten Shells im Nicht-Bourne-Stil, wie csh). Es ist auch ein Kommentar in Python . Und es ist ein Kommentar in einer Vielzahl von Konfigurationsdateien, die überhaupt keine Skripte sind (wie /etc/fstab).

Angenommen, ein Shell-Skript beginnt mit #!/bin/sh. Das ist ein Kommentar, und der Interpreter (die Shell) ignoriert alles in der Zeile nach dem #Zeichen.

Der Zweck einer #!Zeile besteht nicht darin, dem Dolmetscher Informationen zu geben. Der Zweck dieser #!Zeile besteht darin, dem Betriebssystem (oder dem Prozess, durch den der Interpreter gestartet wird) mitzuteilen, was als Interpreter verwendet werden soll .

  • Wenn Sie das Skript beispielsweise durch Ausführen als ausführbare Datei aufrufen ./script.sh, überprüft das System in der ersten Zeile, ob mit #!, gefolgt von null oder mehr Leerzeichen, gefolgt von einem Befehl, begonnen wird. In diesem Fall wird dieser Befehl mit dem Namen des Skripts als Argument ausgeführt. In diesem Beispiel wird es ausgeführt /bin/sh script.sh(oder technisch /bin/sh ./script.sh).

  • Wenn Sie das Skript aufrufen, indem Sie den Interpreter explizit aufrufen, wird die #!Zeile nie konsultiert. Wenn Sie also ausführen sh script.sh, hat die erste Zeile keine Auswirkung. Wenn script2.sh's erste Zeile ist #!/usr/games/nibbles, wird beim Ausführen sh script2.shnicht versucht, das Skript in nibbles(aber ./script2.shwird) zu öffnen .

Sie werden feststellen, dass in keinem Fall die Erweiterung ( .sh) des Skripts , sofern vorhanden, die Ausführung beeinflusst. In einem Unix-ähnlichen System wirkt sich dies normalerweise nicht auf die Ausführung des Skripts aus. Auf einigen anderen Systemen wie Windows wird die #!Shebang-Zeile möglicherweise vollständig vom System ignoriert, und die Erweiterung bestimmt möglicherweise, wie die Skripte ausgeführt werden. (Dies bedeutet nicht, dass Sie Ihre Skripterweiterungen angeben müssen, aber dies ist einer der Gründe, warum sie korrekt sein sollten, wenn Sie dies tun.)

#!wurde ausgewählt, um genau diesem Zweck zu dienen, da # ein Kommentar beginnt. Die #!Zeile ist für das System, nicht für den Interpreter, und sollte vom Interpreter ignoriert werden.

Shebang Line für Bash-Skripte

Sie sagten (ursprünglich), Sie verwenden #!/bin/shfür bashSkripte. Sie sollten dies nur tun, wenn für das Skript keine der bashErweiterungen sherforderlich sind - es muss in der Lage sein, das Skript auszuführen. shist nicht immer ein symlink zu bash. Oft, auch auf allen neueren Debian- und Ubuntu-Systemen , shbesteht ein Symlink zu dash.

Shebang Line für Python-Skripte

Sie sagten auch (in der ersten Version Ihrer Frage vor der Bearbeitung), dass Sie Ihre Python-Skripte mit starten #!/bin/sh read by the interpretor. Wenn du das wörtlich meinst, dann solltest du definitiv damit aufhören. Wenn hello.pymit dieser Zeile begonnen wird, wird Folgendes ./hello.pyausgeführt:

/bin/sh read by the interpretor hello.py

/bin/shwird versuchen, ein Skript auszuführen, das read(mit by the interpretor hello.pyseinen Argumenten) aufgerufen readwird, (hoffentlich) nicht gefunden wird, und Ihr Python-Skript wird von einem Python-Interpreter niemals gesehen.

Wenn Sie diesen Fehler machen, aber das von mir beschriebene Problem nicht haben, rufen Sie wahrscheinlich Ihre Python-Skripte auf, indem Sie explizit den Interpreter angeben (z. B. python hello.py), wodurch die erste Zeile ignoriert wird. Wenn Sie Ihre Skripte an andere verteilen oder später verwenden, ist es möglicherweise nicht klar, dass dies erforderlich ist, damit sie funktionieren. Es ist am besten, sie jetzt zu beheben. Oder entfernen Sie zumindest die erste Zeile vollständig, damit sie ./sinnvoll ist , wenn sie nicht mit der Fehlermeldung ausgeführt werden.

Wenn Sie für Python-Skripte wissen, wo sich der Python-Interpreter befindet (oder befinden wird), können Sie die #!Zeile auf dieselbe Weise schreiben :

#!/usr/bin/python

Wenn es sich um ein Python 3-Skript handelt, sollten Sie Folgendes angeben python3, da pythones sich fast immer um Python 2 handelt :

#!/usr/bin/python3

Allerdings ist das Problem , dass während /bin/shsollte immer vorhanden, und /bin/bashfast immer auf Systeme liegt vor , wenn bashmit dem O kommt, Python kann in einer Vielzahl von Orten existiert.

Daher verwenden viele Python-Programmierer stattdessen Folgendes:

#!/usr/bin/env python

(Oder #!/usr/bin/env python3für Python 3.)

Dies macht das Skript darauf angewiesen, am env"richtigen Ort" zu sein, anstatt sich darauf zu verlassen, am pythonrichtigen Ort zu sein. Das ist gut so, denn:

  • envbefindet sich fast immer in /usr/bin.
  • Auf den meisten Systemen, je nachdem , was python sollte Ihr Skript ausgeführt ist derjenige, der zuerst in der erscheint PATH. Beginnen Sie hello.pymit #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, was (praktisch) dem Ausführen entspricht python hello.py.

Der Grund, den Sie nicht verwenden können, #!pythonist folgender:

  • Sie möchten, dass der angegebene Interpreter durch einen absoluten Pfad angegeben wird (dh beginnend mit /).
  • Der aufrufende Prozess würde python im aktuellen Verzeichnis ausgeführt . Das Durchsuchen des Pfads, wenn der Befehl keinen Schrägstrich enthält, ist ein spezifisches Shell-Verhalten.

Gelegentlich wird ein Python oder andere Skript , das nicht ein Shell - Skript ist eine Shebang - Zeile mit dem Starten haben , #!/bin/sh ...wo ...ein anderer Code. Dies ist manchmal richtig, da es einige Möglichkeiten gibt, die Bourne-kompatible shell ( sh) mit Argumenten aufzurufen, damit sie einen Python-Interpreter aufruft. (Eines der Argumente wird wahrscheinlich enthalten python.) Für die meisten Zwecke #!/usr/bin/env pythonist es jedoch einfacher, eleganter und es ist wahrscheinlicher, dass es so funktioniert, wie Sie es möchten.

Shebang Lines in anderen Sprachen

Viele Programmier- und Skriptsprachen sowie einige andere Dateiformate werden #als Kommentar verwendet. Für jede von ihnen kann eine Datei in der Sprache von einem Programm ausgeführt werden, das sie als Argument verwendet, indem das Programm in der ersten Zeile danach angegeben wird #!.

In einigen Programmiersprachen #ist dies normalerweise kein Kommentar, aber als Sonderfall wird die erste Zeile ignoriert, wenn sie mit beginnt #!. Dies erleichtert die Verwendung der #!Syntax, obwohl #eine Zeile sonst keinen Kommentar enthält.

Shebang Lines für Dateien, die nicht als Skripte ausgeführt werden

Während es weniger intuitiv ist, kann jede Datei, deren Dateiformat eine erste Zeile aufnehmen kann, die mit #!dem vollständigen Pfad einer ausführbaren Datei beginnt , eine Shebang-Zeile haben. Wenn Sie dies tun und die Datei als ausführbar markiert ist, können Sie sie wie ein Programm ausführen, wodurch sie wie ein Dokument geöffnet wird.

Einige Anwendungen verwenden dieses Verhalten absichtlich. In VMware .vmxdefinieren Dateien beispielsweise virtuelle Maschinen. Sie können eine virtuelle Maschine "ausführen", als wäre es ein Skript, da diese Dateien als ausführbar gekennzeichnet sind und eine Shebang-Zeile aufweisen, die bewirkt, dass sie in einem VMware-Dienstprogramm geöffnet werden.

Shebang Lines für Dateien, die nicht als Scripte ausgeführt werden, sondern sich trotzdem wie Scripte verhalten

rmEntfernt Dateien. Es ist keine Skriptsprache. Eine Datei, die gestartet wird #!/bin/rmund als ausführbar markiert ist, kann jedoch ausgeführt werden. Wenn Sie sie ausführen, rmwird sie aufgerufen und gelöscht.

Dies wird oft als "die Datei löscht sich selbst" konzipiert. Aber die Datei läuft überhaupt nicht. Dies entspricht eher der oben für .vmxDateien beschriebenen Situation .

Da die #!Zeile jedoch die Ausführung eines einfachen Befehls (einschließlich Befehlszeilenargumente) erleichtert, können Sie auf diese Weise einige Skripts ausführen. Als einfaches Beispiel für ein "Skript", das ausgefeilter #!/bin/rmist als:

#!/usr/bin/env tee -a

Dadurch werden Benutzereingaben interaktiv übernommen, zeilenweise an den Benutzer zurückgesendet und an das Ende der "Skript" -Datei angehängt.

Nützlich? Nicht sehr. Konzeptuell interessant? Total! Ja. (Etwas.)

Konzeptionell ähnliche Programmier- / Skripting-Konzepte (nur zum Spaß)

Eliah Kagan
quelle
@ Rinzwind Thx! (Übrigens stammt diese Antwort nicht von irgendwo anders, wenn Sie sich das fragen.)
Eliah Kagan
@Rinzwind Mach dir keine Sorgen, mit 8 Upvotes nach 1 Stunde wird es wahrscheinlich noch viel weiter gehen :-)
guntbert
1
Wenn es immer ignoriert wird, was macht dann das Pythons- -xFlag?
Gerrit
4
@ Gerrit Gute Frage. In allen Sprachen, in denen der Interpreter / Compiler Meldungen mit Zeilennummern ausgibt, wird der Inhalt von Kommentaren ignoriert, Kommentarzeilen werden jedoch weiterhin gezählt . Das Hinzufügen eines Kommentars oder einer Leerzeile vor einer Codezeile führt weiterhin dazu, dass die Zeilennummer dieser Codezeile erhöht wird. -x"überspringe [s] die erste Zeile ..." Die 2. Zeile wird nummeriert 1anstatt 2, die 3. Zeile 2anstatt 3, usw. Deshalb solltest du dieses Flag nicht verwenden. ;) -xist für die Skripterstellung auf nicht-Unix-ähnlichen Betriebssystemen gedacht, deren shebang-ähnliche Syntax nicht mit #(also nicht mit einem Python-Kommentar) beginnt .
Eliah Kagan
4
Wenn der Interpreter in Perl direkt gestartet wird ( perl script.plvs. ./script.pl) , liest der Interpreter die shebang-Zeile, um Flags wie z -w. Es wird jedoch nicht empfohlen, sich auf diese Funktion zu verlassen.
OrangeDog
7

Ein Shebang ist die Zeichenfolge, die aus dem Nummernzeichen und dem Ausrufezeichen (z. B. "#!") Besteht, wenn sie als die ersten beiden Zeichen in der ersten Zeile eines Skripts vorkommt.

Wenn unter * nix-Betriebssystemen ein Skript ausgeführt wird, das mit shebang beginnt, analysiert der Programmlader den Rest der Anfangszeile des Skripts als Interpreter-Direktive. Stattdessen wird das angegebene Interpreterprogramm ausgeführt und als Argument der Pfad übergeben, der ursprünglich verwendet wurde, als versucht wurde, das Skript auszuführen. Zum Beispiel, wenn ein Skript mit dem Pfad "path / to / your-script" benannt ist und mit der folgenden Zeile beginnt:

#!/bin/sh

dann wird der Programmlader angewiesen, das Programm "/ bin / sh" anstelle von zB der Bourne-Shell oder einer kompatiblen Shell auszuführen, wobei als erstes Argument "path / to / your-script" übergeben wird.

Dementsprechend heißt das Skript "path / to / python-script" und beginnt mit der folgenden Zeile:

#!/bin/python

dann wird das geladene Programm angewiesen, das Programm "/ bin / python" anstelle von zB Python-Interpreter auszuführen, wobei als erstes Argument "path / to / python-script" übergeben wird.

Kurz gesagt, "#" wird eine Zeile auskommentieren, während die Zeichenfolge "#!" Das Auftreten der ersten beiden Zeichen in der ersten Zeile eines Skripts hat die oben angegebene Bedeutung.

Einzelheiten finden Sie unter Warum beginnen einige Skripte mit #! ...?

Quelle: Einige Abschnitte dieser Antwort stammen (mit geringfügigen Änderungen) von Shebang (Unix) in der englischen Wikipedia (von Wikipedia-Mitwirkenden ). Dieser Artikel ist unter CC-BY-SA 3.0 lizensiert , ebenso wie der Benutzerinhalt hier auf AU, daher ist diese Ableitung mit Zuschreibung gestattet.

Goran Miskovic
quelle
4

#!wird shebangals die ersten zwei Zeichen in der ersten Zeile eines Skripts bezeichnet. Es wird in Skripten verwendet, um einen Interpreter für die Ausführung anzugeben. Das shebangist für das Betriebssystem (Kernel), nicht für die Shell; es wird also nicht als Kommentar interpretiert.

Mit freundlicher Genehmigung: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

Wenn eine Datei ausführbar ist, aber eigentlich kein ausführbares (binäres) Programm, und eine solche Zeile vorhanden ist, wird das nach #! wird mit dem Skriptnamen und all seinen Argumenten gestartet. Diese beiden Zeichen # und! müssen die ersten zwei Bytes in der Datei sein!

Detaillierte Informationen: http://wiki.bash-hackers.org/scripting/basics#the_shebang

saji89
quelle
0

Nein, es wird nur vom execSystemaufruf des Linux-Kernels verwendet und vom Interpreter als Kommentar behandelt

Wenn Sie auf Bash machen:

./something

Unter Linux ruft dies den execSystemaufruf mit dem Pfad auf ./something.

Diese Zeile des Kernels wird in der Datei aufgerufen, die an folgende Adresse übergeben wird exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Es liest die allerersten Bytes der Datei und vergleicht sie mit #!.

Wenn der Vergleich wahr ist, wird der Rest der Zeile vom Linux-Kernel analysiert, der einen weiteren execAufruf mit Pfad /usr/bin/env pythonund aktueller Datei als erstes Argument ausführt:

/usr/bin/env python /path/to/script.py

Dies funktioniert für alle Skriptsprachen, die #als Kommentarzeichen verwendet werden.

Und ja, Sie können eine Endlosschleife machen mit:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash erkennt den Fehler:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! Zufällig ist es für Menschen lesbar, aber das ist nicht erforderlich.

Wenn die Datei mit unterschiedlichen Bytes gestartet execwürde, würde der Systemaufruf einen anderen Handler verwenden. Der andere wichtigste integrierte Handler ist für ausführbare ELF-Dateien: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, der nach Bytes sucht 7f 45 4c 46(was zufällig auch menschlich ist) lesbar für .ELF). Lassen Sie uns das bestätigen, indem wir die 4 ersten Bytes von lesen /bin/ls, die eine ELF-ausführbare Datei sind:

head -c 4 "$(which ls)" | hd 

Ausgabe:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Wenn der Kernel diese Bytes sieht, nimmt er die ELF-Datei, speichert sie korrekt und startet damit einen neuen Prozess. Siehe auch: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Schließlich können Sie mit dem binfmt_miscMechanismus Ihre eigenen Shebang-Handler hinzufügen . Beispielsweise können Sie einen benutzerdefinierten Handler für .jarDateien hinzufügen . Dieser Mechanismus unterstützt sogar Handler nach Dateierweiterung. Eine andere Anwendung besteht darin , ausführbare Dateien einer anderen Architektur transparent mit QEMU auszuführen .

Ich glaube jedoch nicht, dass POSIX Shebangs spezifiziert: https://unix.stackexchange.com/a/346214/32558 , obwohl es in den Begründungsabschnitten und in der Form "Wenn ausführbare Skripte vom System unterstützt werden, kann etwas sein" erwähnt geschehen".

Ciro Santilli ist ein Schauspieler
quelle