Warum ist das Verhalten der `#!` - Syntax von POSIX nicht spezifiziert?

17

Auf der Seite Shell Command Language der POSIX-Spezifikation:

Wenn die erste Zeile einer Datei mit Shell-Befehlen mit den Zeichen "#!" Beginnt, werden die Ergebnisse nicht angegeben.

Warum ist das Verhalten #!von POSIX nicht spezifiziert? Ich finde es verblüffend, dass etwas so tragbares und weit verbreitetes ein unspezifisches Verhalten haben würde.

Harold Fischer
quelle
1
Standards lassen Dinge unbestimmt, um Implementierungen nicht an bestimmte Verhaltensweisen zu binden. Ein "Login" ist beispielsweise "Die nicht angegebene Aktivität, mit der ein Benutzer Zugriff auf das System erhält."
Kusalananda
2
Da POSIX keine ausführbaren Pfade angibt, ist eine Shebang-Zeile ohnehin nicht portierbar. Ich bin mir nicht sicher, ob es viel bringt, wenn man es trotzdem spezifiziert.
Michael Homer
1
@MichaelHomer, sicherlich nicht? Der Standard könnte festlegen, dass die Zeile einen Pfad enthält, der für den Interpreter verwendet werden soll, auch ohne anzugeben, wie dieser Pfad lauten soll.
ilkkachu
1
@HaroldFischer Außer, dass es nicht von der Shell interpretiert wird, wird es entweder vom Betriebssystemkern (zumindest unter Linux, der diese Unterstützung während der Erstellungszeit tatsächlich deaktivieren kann) oder von einer Bibliothek interpretiert, die die exec()Funktion implementiert . Wenn Sie also gegen mehrere Shells prüfen, wissen Sie nicht wirklich, wie portabel es ist.
Austin Hemmelgarn
2
@HaroldFischer Außerdem ist das Verhalten selbst unter POSIX-kompatiblen Betriebssystemen nicht konsistent. Linux und macOS verhalten sich unterschiedlich: Linux markiert die shebang-Zeile nicht vollständig durch Leerzeichen. Unter macOS darf der Skriptinterpreter kein anderes Skript sein. Siehe auch en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Antworten:

21

Ich denke vor allem, weil:

  • Das Verhalten ist von Implementierung zu Implementierung sehr unterschiedlich. Weitere Informationen finden Sie unter https://www.in-ulm.de/~mascheck/various/shebang/ .

    Es könnte jedoch jetzt eine minimale Teilmenge der meisten Unix-ähnlichen Implementierungen spezifizieren: wie #! *[^ ]+( +[^ ]+)?\n(mit nur Zeichen aus dem Zeichensatz des portablen Dateinamens in diesen ein oder zwei Wörtern), wo das erste Wort ein absoluter Pfad zu einer nativen ausführbaren Datei ist, ist das Ding nicht zu lang und Verhalten nicht spezifiziert, wenn die ausführbare Datei setuid / setgid ist und die Implementierung definiert, ob der Interpreterpfad oder der Skriptpfad argv[0]an den Interpreter übergeben wird.

  • POSIX gibt den Pfad der ausführbaren Dateien sowieso nicht an. Einige Systeme haben Pre-POSIX-Dienstprogramme in /bin/ /usr/binund haben die POSIX-Dienstprogramme an einer anderen Stelle (wie in Solaris 10, wo /bin/shsich eine Bourne-Shell und die POSIX-Shell befindet /usr/xpg4/bin; Solaris 11 hat sie durch ksh93 ersetzt, die mehr mit POSIX kompatibel ist, aber die meisten anderen Werkzeuge in /binsind immer noch alte Nicht-POSIX- Werkzeuge . Einige Systeme sind keine POSIX-Systeme, verfügen jedoch über einen POSIX-Modus / eine POSIX-Emulation. Für POSIX ist lediglich eine dokumentierte Umgebung erforderlich, in der sich ein System POSIX-gerecht verhält.

    Siehe zum Beispiel Windows + Cygwin. Tatsächlich wird bei Windows + Cygwin der Wahnsinn gewürdigt, wenn ein Skript von einer Cygwin-Anwendung aufgerufen wird, jedoch nicht von einer nativen Windows-Anwendung.

    Selbst wenn POSIX den Shebang-Mechanismus spezifizierte, konnte er nicht zum Schreiben von POSIX sh/ sed/ awk...-Skripten verwendet werden (beachten Sie auch, dass der Shebang-Mechanismus nicht zum Schreiben von zuverlässigen sed/ awkSkripten verwendet werden kann, da das Übergeben eines End-of-Option nicht möglich ist Marker).

Nun, die Tatsache, dass es nicht spezifiziert ist, bedeutet nicht, dass Sie es nicht verwenden können (nun, es heißt, Sie sollten nicht mit der ersten Zeile beginnen, #!wenn Sie erwarten, dass es sich nur um einen regulären Kommentar handelt und nicht um eine Pauke), aber dass POSIX Ihnen sonst keine Garantie gibt.

Nach meiner Erfahrung Bauden gibt Ihnen mehr Garantie für Portabilität als Skripte zu schreiben Shell POSIX Weg mit: Abschied von dem sie Bang, schreiben Sie das Skript in POSIX - shSyntax und die Hoffnung , dass was auch immer das Skript ruft eine POSIX - konform ruft shdarauf, das ist Gut, wenn Sie wissen, dass das Skript in der richtigen Umgebung vom richtigen Tool aufgerufen wird, aber nicht anders.

Möglicherweise müssen Sie folgende Aufgaben ausführen:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Wenn Sie auf Windows + Cygwin tragbar sein möchten, können Sie Ihre Datei mit einem Namen haben .batoder .ps1Erweiterung und verwenden einen ähnlichen Trick cmd.exeoder powershell.exezum Aufrufen der Cygwin shauf der gleichen Datei.

Stéphane Chazelas
quelle
Interessanterweise ab Ausgabe 5 : "Das Konstrukt #! Ist für Implementierungen reserviert, die diese Erweiterung bereitstellen möchten. Eine tragbare Anwendung kann #! Nicht als erste Zeile eines Shell-Skripts verwenden; es wird möglicherweise nicht als Kommentar interpretiert."
muru
@muru Wenn das Skript wirklich portabel wäre, würde es auf einem POSIX-System, auf dem POSIX ausgeführt wird sh, keine Hashbang-Zeile benötigen, da es von POSIX ausgeführt würde sh.
Kusalananda
1
@Kusalananda das stimmt nur wenn execlpoder execvpwurden verwendet, oder? Wenn ich verwenden würde execve, würde es zu ENOEXEC führen?
muru
9

[D] Das Verhalten scheint zwischen allen POSIX-Beschwerdeshells konsistent zu sein. Ich sehe hier keine Notwendigkeit für Spielraum.

Du schaust nicht tief genug.

In den 1980er Jahren war dieser Mechanismus nicht de facto standardisiert. Obwohl Dennis Ritchie es implementiert hatte, hatte diese Implementierung die Öffentlichkeit auf der AT & T-Seite des Universums nicht erreicht. Es war praktisch nur öffentlich verfügbar und in BSD bekannt; mit ausführbaren Shell-Skripten, die unter AT & T Unix nicht verfügbar sind. Daher war es nicht sinnvoll, es zu standardisieren. Der Stand der Dinge wird durch dieses zeitgenössische Dokument veranschaulicht, eines von vielen solchen:

Beachten Sie, dass BSD die #! interpreterdirekte Ausführung von Dateien zulässt, während SysV nur die direkte Ausführung von a.out-Dateien zulässt. Dies bedeutet, dass eine Instanz einer der exec…()Routinen in einem BSD-Programm möglicherweise unter SysV geändert werden muss, um den Interpreter auszuführen (typlisch)/bin/sh stattdessen ) für dieses Programm .
- Stephen Frede (1988). "Programmieren auf System X Release Y". Australischer Newsletter der Unix Systems User Group . Band 9. Nummer 4. p. 111.

Ein wichtiger Punkt hierbei ist, dass Sie sich mit Shells befassen, wohingegen die Existenz von ausführbaren Shell-Skripten tatsächlich eine Frage der exec…()Funktionen ist. Was Shells tun, schließt die Vorläufer des ausführbaren Skriptmechanismus ein, der auch heute noch in einigen Shells zu finden ist (und auch heutzutage für die exec…p()Untermenge von Funktionen vorgeschrieben ist), und ist etwas irreführend. Was der Standard in dieser Hinsicht berücksichtigen muss, ist die Funktionsweise exec…()eines interpretierten Skripts, und zu der Zeit, als POSIX ursprünglich erstellt wurde , funktionierte es in einem Großteil des Spektrums der Zielbetriebssysteme überhaupt nicht .

Eine untergeordnete Frage ist , warum ist dies , da nicht standardisiert worden, zumal der magische Zahl Mechanismus für die Skript - Interpreter hatte die Öffentlichkeit in der AT & T - Seite des Universums erreicht und hatte dokumentiert exec…()in dem System 5 Interface Definition , durch die Wende der 1990er Jahre :

Eine Interpreterdatei beginnt mit einer Zeile des Formulars

#! Pfadname [arg]
Dabei ist Pfadname der Pfad des Interpreters und arg ein optionales Argument. Wenn Sie execeine Interpreter-Datei, das Systemexec der angegebene Interpreter.
- exec. System V-Schnittstellendefinition . Band 1. 1991.

Leider ist das Verhalten heute fast so unterschiedlich wie in den 1980er Jahren, und es gibt kein wirklich verbreitetes Verhalten, das standardisiert werden könnte. Einige Unices (beispielsweise HP-UX und FreeBSD) unterstützen keine Skripte als Interpreter für Skripte. Ob es sich bei der ersten Zeile um ein, zwei oder viele durch Leerzeichen getrennte Elemente handelt, hängt von MacOS (und Versionen von FreeBSD vor 2005) und anderen ab. Die maximal unterstützte Pfadlänge variiert. und Zeichen außerhalb des POSIX-Zeichensatzes für portable Dateinamen sind ebenso schwierig wie führende und nachfolgende Leerzeichen. Was das 0., 1. und 2. Argument ist, ist ebenfalls schwierig, mit erheblichen Abweichungen zwischen den Systemen. Einige derzeit POSIX-konforme aber nicht-Unix-Systeme unterstützen solche Mechanismen immer noch nicht, und wenn dies vorgeschrieben ist, werden sie nicht mehr POSIX-konform.

Weitere Lektüre

JdeBP
quelle
1

Wie in einigen anderen Antworten erwähnt, variieren die Implementierungen. Dies erschwert die Standardisierung und Aufrechterhaltung der Abwärtskompatibilität mit vorhandenen Skripten. Dies gilt auch für moderne POSIX-Systeme. Zum Beispiel tokenisiert Linux die Shebang-Zeile nicht vollständig durch Leerzeichen. Unter macOS darf der Skriptinterpreter kein anderes Skript sein.

Siehe auch http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
quelle