Ich stelle fest, dass einige Skripte, die ich von anderen erworben habe, den Shebang haben, #!/path/to/NAME
während andere (mit demselben Tool, NAME) den Shebang haben #!/usr/bin/env NAME
.
Beide scheinen richtig zu funktionieren. In Tutorials (zum Beispiel auf Python) scheint es einen Vorschlag zu geben, dass letzterer besser ist. Aber ich verstehe nicht ganz, warum das so ist.
Mir ist klar, dass NAME im PATH enthalten sein muss, um den letztgenannten Shebang zu verwenden, wohingegen der erste Shebang diese Einschränkung nicht hat.
Es scheint mir auch, dass das Erste das bessere ist, da es genau angibt, wo sich NAME befindet. Wenn es in diesem Fall also mehrere Versionen von NAME gibt (z. B. / usr / bin / NAME, / usr / local / bin / NAME), gibt der erste Fall an, welche zu verwenden sind.
Meine Frage ist, warum der erste Schebang dem zweiten vorgezogen wird.
quelle
Antworten:
Es ist nicht unbedingt besser.
Der Vorteil
#!/usr/bin/env python
ist, dass es diepython
ausführbare Datei verwendet, die zuerst im Benutzer angezeigt wird$PATH
.Der Nachteil der
#!/usr/bin/env python
ist , dass es , was auch immer verwendetpython
ausführbar erscheint zuerst in dem Benutzer$PATH
.Das bedeutet, dass sich das Skript je nach Ausführung unterschiedlich verhalten kann. Für einen Benutzer wird möglicherweise das verwendet
/usr/bin/python
, das mit dem Betriebssystem installiert wurde. Zum anderen könnte ein Experiment verwendet werden/home/phred/bin/python
, das nicht richtig funktioniert.Und wenn
python
es nur in installiert ist/usr/local/bin
, kann ein Benutzer, der nicht/usr/local/bin
in installiert ist ,$PATH
das Skript nicht einmal ausführen. (Das ist auf modernen Systemen wahrscheinlich nicht allzu wahrscheinlich, könnte aber für einen unklaren Interpreter leicht passieren.)Indem
#!/usr/bin/python
Sie angeben, geben Sie genau an, welcher Interpreter zum Ausführen des Skripts auf einem bestimmten System verwendet wird .Ein weiteres mögliches Problem ist, dass Sie mit diesem
#!/usr/bin/env
Trick keine Argumente an den Intrepreter übergeben können (außer dem Namen des Skripts, der implizit übergeben wird). Dies ist normalerweise kein Problem, kann es aber sein. Viele Perl-Skripte werden mit geschrieben#!/usr/bin/perl -w
, aber diesuse warnings;
ist heutzutage der empfohlene Ersatz. CSH-Skripte sollten verwendet werden#!/bin/csh -f
- CSH-Skripte werden jedoch nicht empfohlen . Es könnte aber auch andere Beispiele geben.Ich habe eine Reihe von Perl-Skripten in einem persönlichen Versionsverwaltungssystem, die ich installiere, wenn ich ein Konto auf einem neuen System einrichte. Ich verwende ein Installationsskript, das die
#!
Zeile jedes Skripts ändert, wenn es in my installiert wird$HOME/bin
. (Ich musste nichts anderes als in#!/usr/bin/perl
letzter Zeit verwenden. Es geht auf Zeiten zurück, in denen Perl oft nicht standardmäßig installiert war.)Ein kleiner Punkt: Der
#!/usr/bin/env
Trick ist wohl ein Missbrauch desenv
Befehls, der ursprünglich (wie der Name schon sagt) dazu gedacht war, einen Befehl in einer veränderten Umgebung aufzurufen. Außerdem hatten einige ältere Systeme (einschließlich SunOS 4, wenn ich mich richtig erinnere) denenv
Befehl nicht in/usr/bin
. Beides dürfte kein wesentliches Problem darstellen.env
funktioniert auf diese Weise, viele Skripte verwenden diesen#!/usr/bin/env
Trick, und Betriebssystemanbieter werden wahrscheinlich nichts tun, um ihn zu beschädigen. Es könnte ein Problem sein , wenn Sie ein Skript nur auf einem wirklich altes System zu laufen, aber dann sind Sie wahrscheinlich , es müssen sowieso ändern.Ein weiteres mögliches Problem (dank Sopalajo de Arrierez für den Hinweis in den Kommentaren) ist, dass Cron-Jobs in einer eingeschränkten Umgebung ausgeführt werden. Insbesondere
$PATH
ist in der Regel so etwas/usr/bin:/bin
. Wenn sich das Verzeichnis mit dem Interpreter also nicht in einem dieser Verzeichnisse befindet, auch wenn es$PATH
in einer Benutzer-Shell standardmäßig vorhanden/usr/bin/env
ist, funktioniert der Trick nicht. Sie können den genauen Pfad angeben oder Ihrer Crontab eine Linie hinzufügen, um sie festzulegen$PATH
(man 5 crontab
für Details).quelle
use v5.12;
dient einige von diesen Zweck. Und#!/usr/bin/env perl5.12
wird fehlschlagen, wenn das System Perl 5.14 aber nicht 5.12 hat. Für Python 2 vs. 3#!/usr/bin/python2
und#!/usr/bin/python3
dürfte funktionieren./usr/local/bin
. Ich weiß zufällig, dass es richtig funktioniert/usr/bin/perl
. Ich habe keine Ahnung, ob es mit jederperl
ausführbaren Datei funktioniert, die zufällige Benutzer in sich haben$PATH
. Vielleicht experimentiert jemand mit einer alten Version von Perl. Da ich angegeben habe#!/usr/bin/perl
, funktioniert mein Skript (das der Benutzer nicht unbedingt kennt oder kümmert, ist es ein Perl-Skript) nicht mehr./usr/bin/perl
, werde ich es sehr schnell herausfinden und es liegt in der Verantwortung des Systembesitzers / Administrators, es auf dem neuesten Stand zu halten. Wenn Sie mein Skript mit Ihrem eigenen Perl ausführen möchten, können Sie eine Kopie abrufen und ändern oder sie über aufrufenperl foo
. (Und Sie könnten die Möglichkeit in Betracht ziehen, dass die 55 Personen, die diese Antwort positiv bewertet haben, auch ein oder zwei Dinge wissen. Es ist sicher möglich, dass Sie Recht haben und dass sie alle Unrecht haben, aber ich wette nicht so.)Denn / usr / bin / env kann Ihre interpretieren
$PATH
, wodurch Skripte portabler werden.Führt Ihr Skript nur aus, wenn Python in / usr / local / bin installiert ist.
Wird Ihre interpretieren
$PATH
und finden Sie Python in einem beliebigen Verzeichnis in Ihrem$PATH
.So ist Ihr Skript portabler und funktioniert ohne Änderungen auf Systemen, auf denen Python als
/usr/bin/python
oder/usr/local/bin/python
oder sogar benutzerdefinierte Verzeichnisse (die hinzugefügt wurden$PATH
) wie installiert sind/opt/local/bin/python
.Portabilität ist der einzige Grund, weshalb die Verwendung von
env
Pfaden gegenüber fest codierten Pfaden bevorzugt wird.quelle
python
ausführbare Dateien sind besonders häufig, wenn dievirtualenv
Verwendung zunimmt.#! python
, warum wird das nicht verwendet?#! python
wird nicht verwendet, da Sie sich im selben Verzeichnis wie die Python-Binärdatei befinden müssten, da das Barewordpython
als vollständiger Pfad zur Datei interpretiert wird. Wenn Sie keine Python-Binärdatei im aktuellen Verzeichnis haben, wird eine Fehlermeldung wie angezeigtbash: ./script.py: python: bad interpreter: No such file or directory
. Es ist das gleiche, als ob Sie verwendet#! /not/a/real/path/python
Die Angabe des absoluten Pfades ist auf einem bestimmten System genauer. Der Nachteil ist, dass es zu präzise ist. Nehmen wir an , dass das System die Installation von Perl zu alt ist für Ihre Skripte und Sie möchten Ihre eigene stattdessen verwenden: dann müssen Sie die Skripte bearbeiten und ändern
#!/usr/bin/perl
zu#!/home/myname/bin/perl
. Schlimmer noch, wenn Sie Perl/usr/bin
auf einigen Rechnern,/usr/local/bin
auf anderen und/home/myname/bin/perl
noch auf anderen Rechnern installiert haben, müssen Sie drei separate Kopien der Skripte verwalten und auf jedem Rechner die entsprechende ausführen.#!/usr/bin/env
bricht, wennPATH
schlecht ist, tut aber so ziemlich alles. Der Versuch, mit einem schlechten zu arbeiten,PATH
ist sehr selten sinnvoll und zeigt an, dass Sie nur sehr wenig über das System wissen, auf dem das Skript ausgeführt wird, sodass Sie sich sowieso nicht auf einen absoluten Pfad verlassen können.Es gibt zwei Programme, auf deren Speicherort Sie sich bei fast jeder Unix-Variante verlassen können:
/bin/sh
und/usr/bin/env
. Einige obskure und meist pensionierte Unix-Varianten hatten/bin/env
keine/usr/bin/env
, aber es ist unwahrscheinlich, dass Sie ihnen begegnen. Moderne Systeme haben/usr/bin/env
gerade wegen ihrer weit verbreiteten Verwendung in Pony./usr/bin/env
Darauf können Sie zählen.Abgesehen davon
/bin/sh
sollten Sie in einem Shebang nur dann einen absoluten Pfad verwenden, wenn Ihr Skript nicht als portabel gedacht ist, sodass Sie sich auf einen bekannten Speicherort für den Interpreter verlassen können. Beispielsweise kann ein Bash-Skript, das nur unter Linux funktioniert, problemlos verwendet werden#!/bin/bash
. Ein Skript, das nur für die interne Verwendung vorgesehen ist, kann sich auf Standortkonventionen für Hausinterpreter stützen.#!/usr/bin/env
hat Nachteile. Es ist flexibler als die Angabe eines absoluten Pfades, erfordert jedoch die Kenntnis des Interpreten-Namens. Gelegentlich möchten Sie möglicherweise einen Interpreter ausführen, der sich nicht an der entsprechenden Stelle befindet$PATH
, z. B. relativ zum Skript. In solchen Fällen können Sie häufig ein mehrsprachiges Skript erstellen, das sowohl von der Standard-Shell als auch von Ihrem gewünschten Interpreter interpretiert werden kann. So können Sie beispielsweise ein Python 2-Skript sowohl auf Systeme portieren, auf denenpython
Python 3 undpython2
Python 2 vorhanden sind, als auch auf Systeme, auf denenpython
Python 2 vorhanden ist undpython2
die nicht vorhanden sind:quelle
Insbesondere für Perl ist die Verwendung
#!/usr/bin/env
aus zwei Gründen eine schlechte Idee.Erstens ist es nicht portabel. Auf einigen unbekannten Plattformen befindet sich env nicht in / usr / bin. Zweitens, wie Keith Thompson bemerkt hat, kann es Probleme bereiten, Argumente auf der Shebang-Linie weiterzuleiten. Die maximal tragbare Lösung lautet:
Ausführliche Informationen zur Funktionsweise finden Sie unter "perldoc perlrun" und den darin enthaltenen Informationen zum Argument -x.
quelle
Der Grund, warum es einen Unterschied zwischen den beiden gibt, liegt darin, wie Skripte ausgeführt werden.
Die Verwendung von
/usr/bin/env
(die, wie in anderen Antworten erwähnt, in/usr/bin
einigen Betriebssystemen nicht enthalten ist) ist erforderlich, da Sie nicht einfach einen ausführbaren Namen nach dem setzen können#!
- es muss sich um einen absoluten Pfad handeln. Dies liegt daran, dass der#!
Mechanismus auf einer niedrigeren Ebene als die Shell arbeitet. Es ist Teil des Binärladers des Kernels. Dies kann getestet werden. Fügen Sie dies in eine Datei ein und markieren Sie sie als ausführbar:Sie werden feststellen, dass der folgende Fehler ausgegeben wird, wenn Sie versuchen, ihn auszuführen:
Wenn eine Datei als ausführbar markiert ist und mit einem beginnt
#!
, sucht der Kernel (der nichts über$PATH
das aktuelle Verzeichnis weiß : dies sind User-Land-Konzepte) nach einer Datei unter Verwendung eines absoluten Pfads. Da die Verwendung eines absoluten Pfads problematisch ist (wie in anderen Antworten erwähnt), hat sich jemand einen Trick ausgedacht: Sie können/usr/bin/env
(fast immer an diesem Ort) ausführen, um etwas mit auszuführen$PATH
.quelle
Es gibt zwei weitere Probleme bei der Verwendung
#!/usr/bin/env
Es löst nicht das Problem, den vollständigen Pfad zum Interpreter anzugeben, sondern verschiebt ihn lediglich nach
env
.env
ist nicht mehr garantiert,/usr/bin/env
alsbash
garantiert ist,/bin/bash
oder Python in/usr/bin/python
.env
Überschreibt ARGV [0] mit dem Namen des Interpreters (z. B. Bash oder Python).Dies verhindert, dass der Name Ihres Skripts in z. B. der
ps
Ausgabe angezeigt wird (oder ändert, wie / wo er angezeigt wird), und macht es unmöglich, ihn zu finden, z. B. mitps -C scriptname.sh
[Update 04.06.2016]
Und ein drittes Problem:
Das Ändern Ihres PFADS ist mehr als nur das Bearbeiten der ersten Zeile eines Skripts, insbesondere wenn das Erstellen von Skripts für solche Änderungen trivial ist. z.B:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Das Anhängen oder Voranhängen eines Verzeichnisses an $ PATH ist ziemlich einfach (obwohl Sie eine Datei noch bearbeiten müssen, um sie dauerhaft zu machen - Ihre
~/.profile
oder was auch immer - und es ist alles andere als einfach, DIESES Skript zu erstellen, da der PATH an einer beliebigen Stelle im Skript festgelegt werden könnte nicht in der ersten Zeile).Das Ändern der Reihenfolge von PATH-Verzeichnissen ist erheblich schwieriger und weitaus schwieriger als das bloße Bearbeiten der
#!
Zeile.Und Sie haben immer noch alle anderen Probleme, die bei der Verwendung auftreten
#!/usr/bin/env
.@jlliagre schlägt in einem Kommentar vor, der
#!/usr/bin/env
zum Testen Ihres Skripts mit mehreren Interpreter-Versionen nützlich ist, "indem nur deren PATH / PATH-Reihenfolge geändert wird".Wenn Sie das tun müssen, ist es viel einfacher, nur mehrere
#!
Zeilen am oberen Rand Ihres Skripts zu haben (sie sind nur Kommentare an einer beliebigen Stelle außer der allerersten Zeile) und diejenige auszuschneiden / zu kopieren und einzufügen, die Sie jetzt verwenden möchten die erste Zeile.Dies
vi
ist so einfach, als würden Sie den Cursor auf die#!
gewünschte Zeile bewegen und danndd1GP
oder eingebenY1GP
. Selbst mit einem so einfachen Editornano
würde es Sekunden dauern, die Maus zum Kopieren und Einfügen zu verwenden.Insgesamt sind die Vorteile der Verwendung
#!/usr/bin/env
bestenfalls minimal und überwiegen sicherlich nicht annähernd die Nachteile. Sogar der Vorteil "Bequemlichkeit" ist größtenteils illusorisch.IMO, es ist eine dumme Idee, die von einem bestimmten Programmierer vertreten wird, der der Meinung ist, dass Betriebssysteme nichts sind, mit dem man arbeiten kann, sondern ein Problem , das man umgehen (oder das man bestenfalls ignoriert).
PS: Hier ist ein einfaches Skript, mit dem Sie den Interpreter mehrerer Dateien gleichzeitig ändern können.
change-shebang.sh
:Führen Sie es aus wie zB
change-shebang.sh python2.7 *.py
oderchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
quelle
csh
Skripten eine schlechte Lösung: Es funktioniert, aber es gibt viel bessere Alternativen.#!
Zeile ändern ./usr/bin/env
und ich brauche lokal davon loszuwerden, oder , wenn sie es nicht benutzen , und ich brauche , um es hinzuzufügen. In beiden Fällen kann es vorkommen, dass die in dieser Antwort bereitgestellten Skripts ein möglicherweise nützliches Werkzeug in der Toolbox darstellen.Hier ein weiteres Beispiel hinzufügen:
Das Verwenden von
env
ist auch nützlich, wenn Sie Skriptsrvm
beispielsweise für mehrere Umgebungen freigeben möchten .Wenn Sie dies in der cmd-Zeile ausführen, wird angezeigt, welche Ruby-Version verwendet wird, wenn
#!/usr/bin/env ruby
sie in einem Skript verwendet wird:env ruby --version
Daher können Sie bei Verwendung
env
von rvm verschiedene Ruby-Versionen verwenden, ohne Ihre Skripte zu ändern.quelle
Wenn Sie nur für sich selbst oder für Ihre Arbeit schreiben und der Ort des Dolmetschers, den Sie anrufen, immer am selben Ort ist, verwenden Sie auf jeden Fall den direkten Pfad. In allen anderen Fällen verwenden
#!/usr/bin/env
.Hier ist der Grund: In Ihrer Situation befand sich der
python
Interpreter an derselben Stelle, unabhängig von der verwendeten Syntax, aber für viele Benutzer könnte er an einer anderen Stelle installiert sein. Obwohl sich die meisten großen Programmierinterpreter in/usr/bin/
einer Vielzahl neuerer Software befinden, wird dies standardmäßig verwendet/usr/local/bin/
.Ich würde sogar behaupten, immer zu verwenden,
#!/usr/bin/env
denn wenn Sie mehrere Versionen desselben Interpreters installiert haben und nicht wissen, wie Ihre Shell standardmäßig vorgeht, sollten Sie das wahrscheinlich beheben.quelle
Aus Gründen der Portabilität und Kompatibilität ist die Verwendung besser
Anstatt von
Es gibt mehrere Möglichkeiten, in denen sich eine Binärdatei auf einem Linux / Unix-System befinden kann. Auf der Hilfeseite hier (7) finden Sie eine detaillierte Beschreibung der Dateisystemhierarchie.
FreeBSD installiert beispielsweise alle Programme, die nicht zum Basissystem gehören, in / usr / local / . Da die Bash nicht Teil des Basissystems ist, wird die Bash-Binärdatei unter / usr / local / bin / bash installiert .
Wenn Sie ein portables bash / csh / perl / whatever-Skript benötigen, das unter den meisten Linux-Distributionen und FreeBSD ausgeführt wird, sollten Sie #! / Usr / bin / env verwenden .
Beachten Sie auch, dass die meisten Linux-Installationen die env-Binärdatei auch (fest) mit / bin / env oder softlinked / usr / bin in / bin verknüpft haben, was im shebang nicht verwendet werden sollte . Verwenden Sie also nicht #! / Bin / env .
quelle