Kann ich einen variablen Inhalt als Shebang verwenden? [geschlossen]

9

Ich habe ein Shell-Skript, in dem ich einen Shebang hinzufügen möchte. Gegeben eine Variable wie folgt definiert:

SHEBANG="#!/bin/sh"

Meine Frage ist, ob ich diese Variable in einem anderen Skript wie diesem verwenden kann:

$SHEBANG
# other stuff...
Marco Favorito
quelle
5
Ich verstehe den Plan nicht. Wofür soll das gut sein? Ein Skript aus einem anderen Skript generieren? Die Shebang-Zeile wird vom Kernel gelesen.
Hauke ​​Laging
Sie haben es wahrscheinlich nicht versucht, oder? Weil ich withc gemacht habe export SHEBANG='#!/bin/tcsh'und es mit message fehlschlägt, #!/bin/tcsh: No such file or directoryobwohl / bin / tcsh existiert.
Jaroslav Kucera
@ JaroslavKucera: Sie haben es wahrscheinlich so ausgeführt, dass Ihre Shell direkt darauf ausgeführt wurde. Es passierte also genau dasselbe, was passieren würde, wenn Sie $SHEBANGspäter in einem Bash- oder TCSh-Skript eine eigene Zeile einfügen oder sie interaktiv ausführen würden. /bin/tcshist ein gültiger Pfad, aber #!/bin/tcshnicht, es sei denn, Sie werden zufällig in ein Verzeichnis kopiert, in dem ./#!sich ein Verzeichnis oder ein Symlink befindet.
Peter Cordes

Antworten:

12

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 erzeugt wird $SHEBANG "$@". #!In diesem Fall müssen Sie die Variable weglassen . #!ist nur nützlich, wenn es buchstäblich die ersten zwei Bytes einer Datei sind.

  • Eine andere Möglichkeit besteht darin, die Tatsachesh zu nutzen , dass Skripte ohne Shebang-Zeilen (im Allgemeinen) als Shell-Skripte mit einer oder 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önnen Sie sie zum Laufen bringen. Diese Strategie ist oder war bei Lisp-Dolmetschern üblich. In Ihrem Beispiel, in dem sich auch der Interpreter befindet sh, ist dies nicht möglich.

  • Sie können die Dinge in dieser Zeile noch weiter vorantreiben, wenn Sie sich darauf verlassen können, dass bashsie vorhanden sind: ein Skript, dessen erste Zeile lautet

    exec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"

    wird für die meisten Dolmetscher funktionieren; Bash wird als Zwischenschritt verwendet, damit die Prozessersetzung verwendet werden kann, tailum die erste Zeile für uns zu überspringen, damit wir nicht in einer Endlosschleife enden. Einige Interpreter verlangen, dass die Datei durchsuchbar ist, und sie akzeptieren eine solche Pipeline nicht.


Es lohnt sich zu überlegen, ob Sie das wirklich tun möchten. Abgesehen von Sicherheitsbedenken ist es einfach nicht sehr nützlich: Bei wenigen Skripten können die Interpreten so geändert werden, und wo sie können, wären Sie mit ziemlicher Sicherheit besser dran mit einer eingeschränkteren Logik, die die richtige für die Situation hervorgebracht hat (Vielleicht ein Shell-Skript dazwischen, das den Interpreter mit dem Pfad zum realen Skript als Argument aufruft).


* Nicht immer; Unter bestimmten Umständen lesen einige Shells es selbst, aber sie führen keine variable Erweiterung durch.

Michael Homer
quelle
1
So ziemlich immer ist es execve (), das den Dateinamen erhält und das ist ein Systemaufruf (keine Bibliothek),
Jasen
@Jasen Das stimmt, aber viele Shells haben Problemumgehungen für eine fehlende #!Zeile. Wenn execvedies ENOEXECaufgrund einer fehlenden #!Zeile fehlschlägt, kann die Shell auf andere Weise entscheiden, welcher Interpreter verwendet werden soll. Auf diese sollte man sich jedoch nicht verlassen, da verschiedene Shells unterschiedliche Interpreter für dasselbe Skript auswählen können.
Kasperd
3

Ich weiß nicht, ob dies der Anwendungsfall ist, an den Sie denken, aber eine Sache, die Sie tun können , ist

#!/usr/bin/env python

so dass Ihr Skript verwendet die erste pythonin $PATHanstatt zu hart Code benötigen /usr/bin/python, die nicht gut auf irgendeinem System funktionieren könnten , wo die „gute“ Version von Python in ist /usr/local/binoder /optoder was auch immer.

Warum ist es besser, "#! / Usr / bin / env NAME" anstelle von "#! / Path / to / NAME" als Shebang zu verwenden?


Dadurch wird keine Endlosschleife erstellt, da der Python-Interpreter (der envdie Datei aufruft) die #!Zeile nur als Kommentar behandelt. Dies ist eine Art Punkt des #!Entwurfs, da viele Sprachen #als Kommentarzeichen verwendet werden.

Um dies zu erweitern, können Sie ein Polyglot-Programm schreiben, das zunächst unter ausgeführt #!/bin/shwird, dann aber herausfindet, welcher Interpreter tatsächlich verwendet werden soll, und dieses im selben Skript aufruft.

Es stellt sich heraus, dass es bereits eine Webseite mit mehrzeiligen Shebang-Tricks für viele Sprachen gibt, auch für kompilierte Sprachen (das Skript gibt einen Teil von sich an einen Compiler weiter und führt die Ausgabe aus).


Die perlrunManpage gibt 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/shSkript ausgeführt wird, wird es evalin perl -x -wSIhrem Skript ausgeführt und gibt Ihre Argumente weiter.

Wenn Perl es ausführt, evalwird das von der if 0;in der nächsten Zeile gesteuert , so dass es nie ausgeführt wird. Im Gegensatz dazu shwerden Perl-Anweisungen nicht durch einen Zeilenumbruch beendet.

Sie können dies komplexer gestalten, indem Sie "$SHEBANG"anstelle von perloder sogar mehrere shBefehle ausführen, um einen zu verwendenden Perl-Interpreter auszuwählen.

Peter Cordes
quelle
1

Ich denke, das ist was du willst:

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:

  1. Verwenden Sie \$shell_varfile act als "Variable", um die gewünschte Shell zu definieren.

  2. readlink "/proc/$$/exe"druckt die aktuelle Shell .

  3. Da dash keine function f { echo hello world; }Syntax unterstützt , wurde ein Fehler erzeugt function: not found, aber wenn Sie die Shell in bash in \$shell_varfile ändern, tritt kein Fehler mehr auf.

  4. Der vollständige Dateipfad (oder der relative Pfad, wenn ./hole.sh in / tmp ausgeführt wird) wird $@an die \$shell_varDatei übergeben und /tmp/hole.shim Inneren ausgeführt \$shell_var.

Obst
quelle