Ich frage mich, ob es eine allgemeine Möglichkeit gibt, mehrere Optionen über die shebang-Zeile ( #!
) an eine ausführbare Datei zu übergeben .
Ich benutze NixOS und der erste Teil des Shebangs in jedem Skript, das ich schreibe, ist normalerweise /usr/bin/env
. Das Problem, auf das ich stoße, ist, dass alles, was danach kommt, vom System als einzelne Datei oder ein einzelnes Verzeichnis interpretiert wird.
Angenommen, ich möchte ein Skript schreiben, das bash
im posix-Modus ausgeführt wird . Die naive Art, den Shebang zu schreiben, wäre:
#!/usr/bin/env bash --posix
Der Versuch, das resultierende Skript auszuführen, führt jedoch zu folgendem Fehler:
/usr/bin/env: ‘bash --posix’: No such file or directory
Mir ist dieser Beitrag bekannt , aber ich habe mich gefragt, ob es eine allgemeinere und sauberere Lösung gibt.
EDIT : Ich weiß, dass es für Guile- Skripte einen Weg gibt, um das zu erreichen, was ich will, dokumentiert in Abschnitt 4.3.4 des Handbuchs:
#!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#
Der Trick dabei ist, dass die zweite Zeile (beginnend mit exec
) als Code interpretiert wird, sh
aber im Block #!
... !#
als Kommentar enthalten ist und daher vom Guile-Interpreter ignoriert wird.
Wäre es nicht möglich, diese Methode auf einen Interpreter zu verallgemeinern?
Second EDIT : Nach einigem Herumspielen scheint für Interpreten, die ihre Eingaben lesen können stdin
, die folgende Methode zu funktionieren:
#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;
Es ist jedoch wahrscheinlich nicht optimal, da der sh
Prozess so lange dauert, bis der Dolmetscher seine Arbeit beendet hat. Jede Rückmeldung oder Anregung wäre willkommen.
Antworten:
Es gibt keine allgemeine Lösung, zumindest nicht, wenn Sie Linux unterstützen müssen, da der Linux-Kernel alles, was auf das erste „Wort“ in der shebang-Zeile folgt, als ein einziges Argument behandelt .
Ich bin mir nicht sicher, welche Einschränkungen NixOS hat, aber normalerweise schreibe ich einfach Ihren shebang als
oder, wo möglich, Optionen im Skript festlegen :
Alternativ können Sie das Skript mit dem entsprechenden Shell-Aufruf neu starten lassen:
Dieser Ansatz kann auf andere Sprachen verallgemeinert werden, sofern Sie einen Weg finden, die ersten Zeilen (die von der Shell interpretiert werden) von der Zielsprache zu ignorieren.
GNU
coreutils
'env
bietet eine Abhilfe seit Version 8.30 finden Sie unode ‚s Antwort für Details. (Dies ist in Debian 10 und höher, RHEL 8 und höher, Ubuntu 19.04 und höher usw. verfügbar.)quelle
Obwohl nicht gerade portabel, ab Coreutils 8.30 und entsprechend der Dokumentation verwenden:
So gegeben:
Sie erhalten:
und falls Sie neugierig sind,
showargs
ist:quelle
env
wo sie-S
2005 hinzugefügt wurde. Siehe lists.gnu.org/r/coreutils/2018-04/msg00011.htmlshowargs
: pastebin.com/q9m6xr8H und pastebin.com/gS8AQ5WA ( Einzeiler )env
ein eigenesshowargs
: die Option -v, z. B.#!/usr/bin/env -vS --option1 --option2 ...
Der POSIX-Standard beschreibt sehr knapp
#!
:Aus dem Abschnitt Begründung der Dokumentation der
exec()
Familie der Systemschnittstellen :Aus dem Shell-Einführungsabschnitt :
Dies bedeutet im Grunde, dass jede Implementierung (das von Ihnen verwendete Unix) die Spezifika des Parsens der Shebang-Linie nach Belieben ausführen kann.
Einige Unices wie macOS (können ATM nicht testen) teilen die Argumente, die dem Interpreter in der Shebang-Zeile übergeben werden, in separate Argumente auf, während Linux und die meisten anderen Unices dem Interpreter die Argumente als einzelne Option geben.
Es ist daher unklug, sich darauf zu verlassen, dass die Shebang-Linie mehr als ein einziges Argument vertritt.
Siehe auch den Abschnitt Portabilität des Shebang-Artikels auf Wikipedia .
Eine einfache Lösung, die für jedes Dienstprogramm oder jede Sprache verallgemeinerbar ist, besteht darin, ein Wrapper-Skript zu erstellen, das das eigentliche Skript mit den entsprechenden Befehlszeilenargumenten ausführt:
Ich glaube nicht, dass ich persönlich versuchen würde, es selbst wieder ausführen zu lassen , da sich das etwas fragil anfühlt.
quelle
Der Shebang ist in
execve
(2) Manpage wie folgt beschrieben:In dieser Syntax werden zwei Leerzeichen akzeptiert:
Beachten Sie, dass ich bei der Angabe eines optionalen Arguments weder den Plural noch die oben angegebene Syntax verwendet habe
[optional-arg ...]
, da Sie höchstens ein einziges Argument angeben können .Für das Shell-Scripting können Sie das verwenden
set
integrierten Befehl am Anfang Ihres Skripts verwenden, mit dem Sie Interpreter-Parameter festlegen können. Dies führt zu demselben Ergebnis wie bei Verwendung von Befehlszeilenargumenten.In Ihrem Fall:
Überprüfen Sie an einer Bash-Eingabeaufforderung die Ausgabe von
help set
, um alle verfügbaren Optionen abzurufen.quelle
Unter Linux ist der Shebang nicht sehr flexibel. Laut mehreren Antworten ( Stephen Kitts Antwort und Jörg W. Mittag ) gibt es keine Möglichkeit, mehrere Argumente in einer Shebang-Zeile zu übergeben.
Ich bin nicht sicher, ob es für irgendjemanden von Nutzen sein wird, aber ich habe ein kurzes Skript geschrieben, um das fehlende Feature zu implementieren. Siehe https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa .
Es ist auch möglich, eingebettete Problemumgehungen zu schreiben. Im Folgenden stelle ich vier sprachunabhängige Problemumgehungen vor, die auf dasselbe Testskript angewendet wurden, und das Ergebnis wird jeweils gedruckt. Ich nehme an, dass das Skript ausführbar ist und sich in befindet
/tmp/shebang
.Umhüllen Ihres Skripts mit einem Bash-Heredoc innerhalb der Prozessersetzung
Soweit ich weiß, ist dies der zuverlässigste sprachunabhängige Weg, dies zu tun. Es erlaubt das Übergeben von Argumenten und erhält die Standardeinstellung. Der Nachteil ist, dass der Interpreter den (realen) Speicherort der gelesenen Datei nicht kennt.
Der Aufruf
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
druckt:Beachten Sie, dass die Prozessersetzung eine spezielle Datei erzeugt. Dies ist möglicherweise nicht für alle ausführbaren Dateien geeignet. Zum Beispiel
#!/usr/bin/less
beschwert sich:/dev/fd/63 is not a regular file (use -f to see it)
Ich weiß nicht, ob es möglich ist, Heredoc innerhalb der Prozessersetzung im Bindestrich zu haben.
Packen Sie Ihr Skript in einen einfachen Heredoc
Kürzer und einfacher, aber Sie können nicht über
stdin
Ihr Skript darauf zugreifen, und der Interpreter muss in der Lage sein, ein Skript zu lesen und auszuführenstdin
.Der Aufruf
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
druckt:Benutze awk
system()
call aber ohne ArgumenteÜbergibt den Namen der ausgeführten Datei korrekt, aber Ihr Skript empfängt nicht die Argumente, die Sie ihm geben. Beachten Sie, dass awk die einzige mir bekannte Sprache ist, deren Interpreter standardmäßig unter Linux installiert ist und deren Anweisungen standardmäßig über die Befehlszeile gelesen werden.
Der Aufruf
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
druckt:Verwenden Sie den
system()
Aufruf awk 4.1+ , sofern Ihre Argumente keine Leerzeichen enthaltenSchön, aber nur, wenn Sie sicher sind, dass Ihr Skript nicht mit Argumenten aufgerufen wird, die Leerzeichen enthalten. Wie Sie sehen, werden Ihre Argumente, die Leerzeichen enthalten, geteilt, es sei denn, die Leerzeichen werden maskiert.
Der Aufruf
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
druckt:Für awk-Versionen unter 4.1 müssen Sie die Zeichenfolgenverkettung in einer for-Schleife verwenden (siehe Beispielfunktion https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html) .
quelle
$variable
oder zu`command`
ersetzen:exec python3 -O <(cat <<'EOWRAPPER'
Ein Trick für
LD_LIBRARY_PATH
Python in der#!
(shebang) -Linie, der von nichts anderem als der Shell abhängt und sich lohnt:Wie an anderer Stelle auf dieser Seite erklärt, mögen einige Muscheln
sh
können ein Skript für ihre Standardeingabe verwenden.Das Skript, das wir geben,
sh
versucht, den Befehl auszuführen, der''''
durch''
(die leere Zeichenfolge) vereinfacht wird,sh
und führt ihn natürlich nicht aus, da es keinen''
Befehl gibt. Daher wird er normalerweiseline 2: command not found
auf dem Standard-Fehlerdeskriptor ausgegeben, aber wir leiten diese Nachricht mit2>/dev/null
an um Schwarzes Loch am nächsten, weil es für den Benutzer unübersichtlich und verwirrend wäre, es sichsh
anzeigen zu lassen .Wir gehen dann zu dem für uns interessanten Befehl über
exec
, der in unserem Fall den aktuellen Shell-Prozess durch Folgendes ersetzt:/usr/bin/env python
mit den entsprechenden Parametern:"$0"
um python wissen zu lassen, welches script es öffnen und interpretieren soll, und um auch zu setzensys.argv[0]
"$@"
Pythonssys.argv[1:]
auf die in der Skriptbefehlszeile übergebenen Argumente setzen.Und wir bitten Sie auch
env
, dieLD_LIBRARY_PATH
Umgebungsvariable festzulegen, die der einzige Punkt des Hacks ist.Der Shell-Befehl endet mit dem Kommentar, der mit beginnt,
#
sodass die Shell die nachstehenden dreifachen Anführungszeichen ignoriert'''
.sh
wird dann durch eine shinny neue Instanz des Python - Interpreters ersetzt, die das als erstes Argument angegebene Python - Quellenskript öffnet und liest (das"$0"
) .Python öffnet die Datei und überspringt dank des
-x
Arguments die erste Zeile der Quelle . Hinweis: Es funktioniert auch ohne,-x
da für Python ein Shebang nur ein Kommentar ist .Python interpretiert dann die 2. Zeile als Docstring für die aktuelle Moduldatei. Wenn Sie also einen gültigen Modul-Docstring benötigen, legen Sie einfach als
__doc__
Erstes in Ihrem Python-Programm fest, wie im obigen Beispiel.quelle
''''exec ...
verwerfen. Beachten Sie, dass vor exec kein Leerzeichen steht, da sonst nach dem leeren Befehl gesucht wird. Sie möchten das Leere auf das erste Argument spleißen, damit das so$0
istexec
.Als ich nach einer ausführbaren Datei suchte, die ein Skript als einziges Argument ausnimmt, fand ich eine ziemlich dumme Problemumgehung:
quelle