Was ist der Unterschied zwischen diesen beiden Anweisungen in der Kopfzeile eines Bash-Skripts:
#!/usr/bin/env bash
#!/usr/bin/bash
Wenn ich die env
Manpage konsultiere , erhalte ich folgende Definition:
env - run a program in a modified environment
Was bedeutet das?
Antworten:
Einen Befehl durch Ausführen
/usr/bin/env
hat den Vorteil , auf die Suche nach was auch immer die Standardversion des Programms ist in der aktuellen env ironment.Auf diese Weise müssen Sie nicht an einer bestimmten Stelle im System danach suchen, da sich diese Pfade möglicherweise an verschiedenen Stellen auf verschiedenen Systemen befinden. Solange es auf deinem Weg ist, wird es es finden.
Ein Nachteil ist, dass Sie nicht mehr als ein Argument übergeben können (z. B. nicht schreiben können
/usr/bin/env awk -f
), wenn Sie Linux unterstützen möchten, da POSIX vage ist, wie die Zeile zu interpretieren ist, und Linux alles nach dem ersten interpretiert Leerzeichen, um ein einzelnes Argument zu bezeichnen. Sie können/usr/bin/env -S
einige Versionen von verwendenenv
, um dies zu umgehen, aber dann wird das Skript noch weniger portabel und funktioniert auf relativ neuen Systemen (z. B. sogar Ubuntu 16.04, wenn nicht später).Ein weiterer Nachteil ist, dass Sie, da Sie keine explizite ausführbare Datei aufrufen, möglicherweise Fehler und Sicherheitsprobleme bei Mehrbenutzersystemen haben (wenn es beispielsweise jemandem gelungen ist, die ausführbare Datei
bash
in Ihrem Pfad aufzurufen ).In einigen Situationen kann die erste bevorzugt werden (z. B. das Ausführen von Python-Skripten mit mehreren Python-Versionen, ohne dass die ausführbare Zeile überarbeitet werden muss). In Situationen, in denen Sicherheit im Mittelpunkt steht, wird Letzteres bevorzugt, da dies die Möglichkeiten der Code-Injektion einschränkt.
quelle
#!/usr/bin/env echo Hello
beschwert sich :/usr/bin/env: echo Hello: No such file or directory
. Anscheinend wird esecho Hello
als ein einziges Argument behandelt/usr/bin/env
.env
Befehl können durchaus Argumente an den Befehl übergeben werden. Das Problem ist die Semantik der#!
Zeile, und das hängt vom Kernel ab. Neuere Linux-Kernel erlauben solche Dinge#!/usr/bin/env command args
, ältere Linux-Kernel und andere Systeme jedoch nicht.Mit using
#!/usr/bin/env NAME
sucht die Shell nach der ersten Übereinstimmung von NAME in der Umgebungsvariablen $ PATH. Dies kann nützlich sein, wenn Sie den absoluten Pfad nicht kennen oder nicht danach suchen möchten.quelle
Anstatt den Pfad zum Interpreter explizit wie in zu definieren, wird der Interpreter
/usr/bin/bash/
mithilfe des Befehls env gesucht und von jedem Ort aus gestartet, an dem er zuerst gefunden wird. Dies hat sowohl Vor- als auch Nachteilequelle
Wenn die Shell-Skripte mit beginnen
#!/bin/bash
, werden sie immer mitbash
von ausgeführt/bin
. Wenn sie jedoch mit beginnen#!/usr/bin/env bash
, suchen sie nachbash
in$PATH
und beginnen dann mit dem ersten, den sie finden können.Warum sollte das nützlich sein? Angenommen, Sie möchten
bash
Skripts ausführen , für die bash 4.x oder höher erforderlich ist, auf Ihrem System jedoch nurbash
3.x installiert ist und Ihre Distribution derzeit keine neuere Version anbietet oder Sie kein Administrator sind und nicht ändern können, was auf diesem System installiert ist .Natürlich können Sie Bash-Quellcode herunterladen und Ihre eigene Bash von Grund auf neu erstellen, indem Sie sie
~/bin
beispielsweise platzieren. Sie können Ihre$PATH
Variable auch so ändern.bash_profile
, dass sie~/bin
als erster Eintrag enthalten ist (PATH=$HOME/bin:$PATH
da dies~
nicht erweitert wird$PATH
). Wenn Sie jetzt anrufenbash
, sucht die Shell zuerst in der richtigen$PATH
Reihenfolge danach, also beginnt sie damit~/bin
, wo sie Ihre findetbash
. Dasselbe passiert, wenn Skripte nachbash
Verwendung suchen#!/usr/bin/env bash
, sodass diese Skripte jetzt mit Ihrem benutzerdefiniertenbash
Build auf Ihrem System funktionieren .Ein Nachteil ist, dass dies zu unerwartetem Verhalten führen kann, z. B. dass dasselbe Skript auf demselben Computer mit unterschiedlichen Interpreten für unterschiedliche Umgebungen oder Benutzer mit unterschiedlichen Suchpfaden ausgeführt wird, was zu Kopfschmerzen aller Art führt.
Der größte Nachteil dabei
env
ist, dass einige Systeme nur ein Argument zulassen, sodass Sie dies nicht tun können#!/usr/bin/env <interpreter> <arg>
, da die Systeme<interpreter> <arg>
als ein Argument sehen (sie behandeln es so, als ob der Ausdruck in Anführungszeichen gesetzt wäre) und daherenv
nach einem benannten Interpreter suchen<interpreter> <arg>
. Beachten Sie, dass dies kein Problem desenv
Befehls selbst ist, bei dem immer mehrere Parameter übergeben werden konnten, sondern mit dem Shebang-Parser des Systems, das diese Zeile vor dem Aufruf analysiertenv
. In der Zwischenzeit wurde dies auf den meisten Systemen behoben. Wenn Ihr Skript jedoch extrem portabel sein soll, können Sie sich nicht darauf verlassen, dass dies auf dem System behoben wurde, das Sie ausführen werden.Dies kann sogar Auswirkungen auf die Sicherheit haben, z. B. wenn
sudo
es nicht für die Bereinigung der Umgebung konfiguriert oder$PATH
von der Bereinigung ausgeschlossen wurde. Lassen Sie mich dies demonstrieren:In der Regel
/bin
ist ein gut geschützter Ort, nurroot
kann dort etwas ändern. Ihr Home-Verzeichnis ist jedoch kein Programm, das Sie ausführen, um Änderungen daran vorzunehmen. Das bedeutet, dass bösartiger Code eine Fälschungbash
in ein verstecktes Verzeichnis einfügen kann. Ändern Sie Ihr Verzeichnis so,.bash_profile
dass dieses Verzeichnis in Ihr Verzeichnis aufgenommen wird$PATH
, sodass alle verwendeten Skripte#!/usr/bin/env bash
mit dieser Fälschung ausgeführt werdenbash
. Wenn Siesudo
behalten$PATH
, sind Sie in großen Schwierigkeiten.Angenommen, ein Tool erstellt eine Datei
~/.evil/bash
mit folgendem Inhalt:Lassen Sie uns ein einfaches Skript erstellen
sample.sh
:Proof of Concept (auf einem System, auf dem
sudo
aufbewahrt wird$PATH
):Normalerweise sollten sich die klassischen Shells alle in befinden.
/bin
Wenn Sie sie aus irgendeinem Grund nicht dort platzieren möchten, ist es wirklich kein Problem, einen Symlink zu platzieren/bin
, der auf ihre tatsächlichen Positionen verweist (oder vielleicht/bin
selbst ein Symlink ist) Ich würde immer mit#!/bin/sh
und gehen#!/bin/bash
. Es gibt einfach zu viel, was kaputt gehen würde, wenn diese nicht mehr funktionieren würden. Es ist nicht so, dass POSIX diese Position benötigen würde (POSIX standardisiert keine Pfadnamen und damit überhaupt nicht die Shebang-Funktion), aber sie sind so häufig, dass selbst wenn ein System keine anbieten/bin/sh
würde, es wahrscheinlich immer noch verstehen würde#!/bin/sh
und wissen, was damit zu tun ist und möglicherweise nur aus Gründen der Kompatibilität mit vorhandenem Code.Für modernere, nicht standardmäßige, optionale Interpreter wie Perl, PHP, Python oder Ruby wird jedoch nicht genau angegeben, wo sie sich befinden sollen. Sie werden in können
/usr/bin
aber sie können auch in sein/usr/local/bin
oder in einem ganz anderen Hierarchiezweig (/opt/...
,/Applications/...
, etc.). Deshalb verwenden diese oft die#!/usr/bin/env xxx
Shebang-Syntax.quelle
Ich finde es nützlich, denn als ich nichts über env wusste, bevor ich anfing, ein Skript zu schreiben, tat ich Folgendes:
und dann habe ich diese Zeile in der Datei in shebang geändert.
Ich habe dies getan, weil ich mich nicht immer daran erinnerte, wo sich NodeJS auf meinem Computer befinden - / usr / bin / oder / bin /, daher
env
ist dies für mich sehr nützlich. Vielleicht gibt es Details dazu, aber das ist mein Grundquelle