Ich habe ein Shell-Skript, in das ich ein Shebang einfügen möchte. Gegeben sei eine Variable, die wie folgt definiert ist:
SHEBANG="#!/bin/sh"
Meine Frage ist, ob ich diese Variable in einem anderen Skript wie diesem verwenden kann:
$SHEBANG
# other stuff...
Antwort1
Nein. Die Shebang-Zeile wird von Ihrem Kernel oder Ihrer Systembibliothek* verarbeitet und verarbeitet keine Shell-Variablen.
Wenn Sie aus irgendeinem Grund eine Umgebungsvariable als Interpreter verwenden möchten, können Sie eine ausführbare „Trampolin“-Datei (kein Skript!) erstellen, die erneut startet
$SHEBANG "$@"
. In diesem Fall müssen Sie das#!
aus der Variable weglassen;#!
ist nur nützlich, wenn es sich buchstäblich um die ersten beiden Bytes einer Datei handelt.Eine weitere Möglichkeit ist die Nutzung von
sh
die Tatsache, dass Skripte ohne Shebang-Zeilen (im Allgemeinen) als Shell-Skripte entweder mit oder mit der aufrufenden Shell ausgeführt werden. Wenn es eine Kommentarsyntax für Ihr Shebang-Ziel gibt, die sich sinnvoll mit der Shell-Syntax überschneidet, könnten Sie es zum Laufen bringen. Diese Strategie ist oder war bei Lisp-Interpretern üblich. In Ihrem Beispiel, wo der Interpreter auch istsh
, ist dies nicht praktikabel.Sie können die Dinge auf dieser Zeile noch weiter vorantreiben, wenn Sie sich darauf verlassen können,
bash
dass sie präsent sind: ein Skript, dessen erste Zeile lautetexec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"
funktioniert für die meisten Interpreter; Bash wird als Zwischenschritt verwendet, so dassProzesssubstitutionkann verwendet werden, um
tail
überspringe die erste Zeile für uns, damit wir nicht in einer Endlosschleife landen. Einige Interpreter erfordern, dass die Datei durchsuchbar ist, und sie akzeptieren eine solche Pipeline nicht.
Es lohnt sich, darüber nachzudenken, ob Sie das wirklich tun möchten. Abgesehen von Sicherheitsbedenken ist es einfach nicht sehr nützlich: Bei wenigen Skripten kann man den Interpreter auf diese Weise austauschen, und wo dies möglich ist, wäre man mit Sicherheit mit einer eingeschränkteren Logik besser dran, die die richtige Logik für die jeweilige Situation erzeugt (vielleicht ein Shell-Skript dazwischen, das den Interpreter mit dem Pfad zum echten Skript als Argument aufruft).
* Nicht immer; unter bestimmten Umständen lesen einige Shells es selbst, führen aber trotzdem keine Variablenerweiterung durch.
Antwort2
Ich weiß nicht, ob das der Anwendungsfall ist, den Sie im Sinn haben, aber einesdürfentun ist
#!/usr/bin/env python
Ihr Skript verwendet also das erste python
in, $PATH
anstatt es fest codieren zu müssen /usr/bin/python
, was auf manchen Systemen, auf denen die „gute“ Python-Version in /usr/local/bin
oder /opt
was auch immer vorhanden ist, möglicherweise nicht gut funktioniert.
Warum ist es besser, „#!/usr/bin/env NAME“ statt „#!/path/to/NAME“ als meinen Shebang zu verwenden?
Dies erzeugt keine Endlosschleife, da der Python-Interpreter (der env
die Datei aufruft) die #!
Zeile einfach als Kommentar behandelt. Dies ist sozusagen der Sinn des #!
Entwurfs, da viele Sprachen #
als Kommentarzeichen verwenden.
Um dies zu erweitern,Sie könnten ein polyglottes Programm schreibendas zunächst unter ausgeführt wird #!/bin/sh
, dann aber herausfindet, welcher Interpreter tatsächlich verwendet werden soll, und diesen im selben Skript aufruft.
Es stellt sich heraus, dass es bereitseine Webseite mit mehrzeiligen Shebang-Tricksfür viele Sprachen, einschließlich kompilierter Sprachen (das Skript gibt einen Teil von sich selbst an einen Compiler weiter und führt die Ausgabe aus).
Die perlrun
Manpage bietet dieses Beispiel als Alternative zu #!/usr/bin/env perl
:
#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
if 0;
your perl script goes here
Wenn es als /bin/sh
Skript ausgeführt wird, wird es auf Ihrem Skript eval
ausgeführt perl -x -wS
und übergibt Ihre Argumente.
Wenn Perl es ausführt, wird es durch das in der nächsten Zeile eval
gesteuert , sodass es nie ausgeführt wird. Im Gegensatz zu werden Perl-Anweisungen nicht durch eine neue Zeile beendet.if 0;
sh
Sie können dies noch komplexer gestalten, indem Sie "$SHEBANG"
anstelle von verwenden perl
oder sogar mehrere sh
Befehle ausführen, um einen zu verwendenden Perl-Interpreter auszuwählen.
Antwort3
Ich denke, das ist, was Sie wollen:
xb@dnxb:/tmp$ cat /tmp/hole.sh
#!/usr/bin/$shell_var
echo 1
readlink "/proc/$$/exe"
echo 2
function f { echo hello world; }
echo 3
xb@dnxb:/tmp$ chmod +x /tmp/hole.sh
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var
#!/bin/bash
/bin/dash -- "$@"
xb@dnxb:/tmp$ sudo chmod +x /usr/bin/\$shell_var
xb@dnxb:/tmp$ ./hole.sh
1
/bin/dash
2
./hole.sh: 5: ./hole.sh: function: not found
3
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var
#!/bin/bash
/bin/bash -- "$@"
xb@dnxb:/tmp$ ./hole.sh
1
/bin/bash
2
3
xb@dnxb:/tmp$
Erläuterung:
Verwenden Sie
\$shell_var
die Datei „act“ als „Variable“, um die gewünschte Shell zu definieren.Da Dash nicht unterstützt
function f { echo hello world; }
Syntax, daher trat ein Fehler auffunction: not found
, aber durch Ändern der Shell in\$shell_var
der Datei zu Bash tritt kein Fehler mehr auf.Der vollständige Dateipfad (oder der relative Pfad, wenn ./hole.sh in /tmp ausgeführt wird) wird als Datei übergeben
$@
und\$shell_var
darin/tmp/hole.sh
ausgeführt\$shell_var
.