#! / bin / sh -
Ist (oder war zumindest) oft der empfohlene Knall , um ein Drehbuch interpretieren zu lassen /bin/sh
.
Warum nicht einfach #! /bin/sh
oder #!/bin/sh
?
Wofür ist das -
?
quelle
#! / bin / sh -
Ist (oder war zumindest) oft der empfohlene Knall , um ein Drehbuch interpretieren zu lassen /bin/sh
.
Warum nicht einfach #! /bin/sh
oder #!/bin/sh
?
Wofür ist das -
?
Das hat einen ähnlichen Grund, warum Sie schreiben müssen:
rm -- *.txt
Und nicht
rm *.txt
Es sei denn, Sie können garantieren, dass keine der .txt
Dateien im aktuellen Verzeichnis einen Namen hat, der mit beginnt -
.
Im:
rm <arg>
<arg>
wird als Option betrachtet, wenn es mit -
oder einer Datei beginnt , die sonst entfernt werden soll. Im
rm - <arg>
arg
wird immer als zu entfernende Datei betrachtet, unabhängig davon, ob sie mit -
oder nicht beginnt .
Das ist das gleiche für sh
.
Wenn man ein Skript ausführt, das mit beginnt
#! /bin/sh
Normalerweise mit:
execve("path/to/the-script", ["the-script", "arg"], [environ])
Das System wandelt es um in:
execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])
Das path/to/the-script
ist normalerweise nicht etwas, das unter der Kontrolle des Drehbuchautors steht. Der Autor kann nicht vorhersagen, wo und unter welchem Namen Kopien des Skripts gespeichert werden. Insbesondere können sie nicht garantieren, dass das, path/to/the-script
mit dem es aufgerufen wird, nicht mit -
(oder +
dem auch ein Problem mit sh
) beginnt . Deshalb brauchen wir das -
Hier, um das Ende der Optionen zu markieren.
Zum Beispiel ist auf meinem System zcat
(wie in den meisten anderen Skripten) ein Beispiel für ein Skript, das dieser Empfehlung nicht entsprach:
$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]
Jetzt können Sie fragen, warum #! /bin/sh -
und nicht #! /bin/sh --
?
Während #! /bin/sh --
mit POSIX-Shells arbeiten würde, #! /bin/sh -
ist das portabler; insbesondere zu alten Versionen von sh
. sh
Die Behandlung -
als und das Ende von Optionen als Vorgänger getopt()
und die allgemeine Verwendung von --
, um das Ende von Optionen für eine lange Zeit zu markieren. So wie die Bourne-Shell (aus den späten 70ern) ihre Argumente analysierte, wurde nur das erste Argument für Optionen berücksichtigt, wenn es mit begann -
. Alle nachfolgenden Zeichen -
werden als Optionsnamen behandelt. Wenn es nach dem kein Zeichen -
gab, gab es keine Optionen. Das steckt und alle späteren Bourne-ähnlichen Muscheln erkennen -
als Weg, das Ende von Optionen zu markieren.
In der Bourne-Shell (aber nicht in modernen Bourne-ähnlichen Shells) #! /bin/sh -euf
würde das Problem ebenfalls umgangen, da nur das erste Argument für Optionen berücksichtigt wurde.
Nun könnte man sagen , dass wir hier pedantisch sind zu sein, und es ist auch , warum ich das schrieb Notwendigkeit kursiv oben:
-
oder +
oder mit in einem Verzeichnis , dessen Namen beginnt setzen -
oder +
.execvp()
/ execlp()
. In diesem Fall rufen Sie sie im Allgemeinen entweder the-script
auf, um nachzuschlagen. $PATH
In diesem Fall execve()
beginnt das Pfadargument für den Systemaufruf normalerweise mit /
(weder -
noch +
), oder ./the-script
Sie möchten, dass the-script
das aktuelle Verzeichnis ausgeführt wird (und) dann beginnt der Weg mit ./
, weder -
noch +
).Neben der theoretischen Richtigkeit gibt es noch einen weiteren Grund, warum diese #! /bin/sh -
Methode als bewährte Methode empfohlen wurde . Und das geht auf eine Zeit zurück, in der auf mehreren Systemen noch setuid-Skripte unterstützt wurden.
Wenn Sie ein Skript haben, das Folgendes enthält:
#! /bin/sh
/bin/echo "I'm running as root"
Und dieses Skript war (wie bei den -r-sr-xr-x root bin
Berechtigungen) setuid root auf diesen Systemen, wenn es von einem normalen Benutzer ausgeführt wurde, dem
execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])
würde so gemacht werden root
!
Wenn der Benutzer einen Symlink erstellt /tmp/-i -> path/to/the-script
und ausgeführt hat -i
, startet er eine interaktive Shell ( /bin/sh -i
) als root
.
The -
würde das umgehen (es würde nicht das Race-Condition-Problem umgehen , oder die Tatsache, dass einige sh
Implementierungen wie einige ksh88
-basierte Skriptargumente ohne /
in $PATH
nachschlagen würden).
Heutzutage unterstützt kaum ein System mehr setuid-Skripte, und einige derjenigen, die dies noch tun (normalerweise nicht standardmäßig), führen am Ende einen execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])
(wobei <n>
ein Dateideskriptor zum Lesen des Skripts geöffnet ist) aus, der sowohl dieses als auch das Problem umgeht Rennbedingung.
Beachten Sie, dass bei den meisten Interpreten ähnliche Probleme auftreten, nicht nur bei Bourne-ähnlichen Shells. Nicht-Bourne-ähnliche Shells unterstützen im Allgemeinen nicht -
als End-of-Option-Marker, sondern im Allgemeinen --
(zumindest für moderne Versionen).
Ebenfalls
#! /usr/bin/awk -f
#! /usr/bin/sed -f
Haben das Problem nicht als das nächste Argument als Argument für die gilt -f
Option auf jeden Fall, aber es funktioniert immer noch nicht , wenn path/to/script
ist -
(in diesem Fall mit den meisten sed
/ awk
Implementierungen sed
/ awk
nicht lesen den Code aus dem -
Datei, aber stattdessen von stdin).
Beachten Sie auch, dass auf den meisten Systemen Folgendes nicht verwendet werden kann:
#! /usr/bin/env sh -
#! /usr/bin/perl -w --
Wie bei den meisten Systemen lässt der shebang-Mechanismus nur ein Argument nach dem Interpreterpfad zu.
Ob man #! /bin/sh -
vs benutzt #!/bin/sh -
, ist nur Geschmackssache. Ich bevorzuge das erstere, da es den Interpreterpfad sichtbarer macht und die Mausauswahl erleichtert. Es gibt eine Legende, die besagt, dass der Platz in einigen alten Unix-Versionen benötigt wurde, aber AFAIK, das wurde nie verifiziert.
Eine sehr gute Referenz zum Unix-Shebang finden Sie unter https://www.in-ulm.de/~mascheck/various/shebang
bash
akzeptiert natürlich Optionen wie jede andere Shell. Akzeptiert als Bourne-ähnliche und POSIX-Shellbash
sowohl#! /bin/bash -
als auch#! /bin/bash --
.