Wenn die Shell-Skripte mit #!/bin/bash
beginnen , werden sie immer mit bash
ausgeführt ab /bin
. Wenn sie jedoch mit #!/usr/bin/env bash
beginnen , suchen sie nach bash
in $PATH
und dann mit dem ersten beginnen, den sie finden können.
Warum wäre das nützlich? Angenommen, Sie möchten bash
ausführen Skripte, die bash 4.x oder neuer erfordern, Ihr System aber nur bash
hat 3.x installiert und derzeit bietet Ihre Distribution keine neuere Version an oder Sie sind kein Administrator und können nicht ändern, was auf diesem System installiert ist.
Natürlich können Sie den Bash-Quellcode herunterladen und Ihre eigene Bash von Grund auf neu erstellen, indem Sie sie auf ~/bin
platzieren zum Beispiel. Und Sie können auch Ihren $PATH
ändern Variable in Ihrem .bash_profile
Datei, die ~/bin
enthält als erster Eintrag (PATH=$HOME/bin:$PATH
als ~
wird in $PATH
nicht erweitert ). Wenn Sie jetzt bash
anrufen , sucht die Shell zuerst in $PATH
der Reihe nach, also beginnt es mit ~/bin
, wo es Ihren bash
findet . Dasselbe passiert, wenn Skripte nach bash
suchen mit #!/usr/bin/env bash
, also würden diese Skripte jetzt auf Ihrem System mit Ihrem benutzerdefinierten bash
funktionieren bauen.
Ein Nachteil ist, dass dies zu unerwartetem Verhalten führen kann, z. Dasselbe Skript auf demselben Computer kann mit unterschiedlichen Interpretern für unterschiedliche Umgebungen oder Benutzer mit unterschiedlichen Suchpfaden ausgeführt werden, was alle Arten von Kopfschmerzen verursacht.
Der größte Nachteil bei env
ist, dass einige Systeme nur ein Argument zulassen, also können Sie dies nicht tun #!/usr/bin/env <interpreter> <arg>
, da die Systeme <interpreter> <arg>
sehen als ein Argument (sie behandeln es so, als ob der Ausdruck in Anführungszeichen stünde) und somit env
sucht nach einem Interpreter namens <interpreter> <arg>
. Beachten Sie, dass dies kein Problem des env
ist Befehl selbst, der immer die Übergabe mehrerer Parameter erlaubte, aber mit dem Shebang-Parser des Systems, der diese Zeile analysiert, bevor er überhaupt env
aufruft . Inzwischen wurde dies auf den meisten Systemen behoben, aber wenn Ihr Skript ultraportabel sein soll, können Sie sich nicht darauf verlassen, dass dies auf dem System, das Sie ausführen werden, behoben wurde.
Es kann sogar Auswirkungen auf die Sicherheit haben, z. wenn sudo
wurde nicht konfiguriert, um die Umgebung zu bereinigen oder $PATH
wurde von der Reinigung ausgeschlossen. Lassen Sie mich das demonstrieren:
Normalerweise /bin
ist ein gut geschützter Ort, nur root
kann da was ändern. Ihr Home-Verzeichnis ist jedoch nicht jedes Programm, das Sie ausführen, kann Änderungen daran vornehmen. Das bedeutet, dass bösartiger Code einen gefälschten bash
platzieren könnte Ändern Sie in einem versteckten Verzeichnis Ihren .bash_profile
um dieses Verzeichnis in Ihren $PATH
aufzunehmen , also alle Skripte, die #!/usr/bin/env bash
verwenden wird am Ende mit diesem gefälschten bash
laufen . Wenn sudo
behält $PATH
, du steckst in großen Schwierigkeiten.
Z.B. Betrachten Sie ein Tool, das eine Datei ~/.evil/bash
erstellt mit folgendem Inhalt:
#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "[email protected]"
Lassen Sie uns ein einfaches Skript sample.sh
erstellen :
#!/usr/bin/env bash
echo "Hello World"
Proof of Concept (auf einem System, auf dem sudo
behält $PATH
):
$ ./sample.sh
Hello World
$ sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ sudo ./sample.sh
All your base are belong to us...
Hello World
Normalerweise sollten sich die klassischen Shells alle in /bin
befinden und wenn Sie sie aus irgendeinem Grund nicht dort platzieren möchten, ist es wirklich kein Problem, einen Symlink in /bin
zu platzieren das auf ihre wahren Standorte verweist (oder vielleicht /bin
selbst ist ein Symlink), also würde ich immer mit #!/bin/sh
gehen und #!/bin/bash
. Es gibt einfach zu viele, die kaputt gehen würden, wenn diese nicht mehr funktionieren würden. Es ist nicht so, dass POSIX diese Position erfordern würde (POSIX standardisiert keine Pfadnamen und standardisiert daher überhaupt nicht die Shebang-Funktion), aber sie sind so verbreitet, dass selbst wenn ein System keinen /bin/sh
, würde es wahrscheinlich immer noch #!/bin/sh
verstehen und wissen, was damit zu tun ist, und sei es nur für die Kompatibilität mit vorhandenem Code.
Aber für modernere, nicht standardmäßige, optionale Interpreter wie Perl, PHP, Python oder Ruby ist nirgendwo wirklich angegeben, wo sie sich befinden sollten. Sie können in /usr/bin
sein aber sie können genauso gut in /usr/local/bin
sein oder in einem ganz anderen Hierarchiezweig (/opt/...
, /Applications/...
, etc.). Deshalb verwenden diese oft den #!/usr/bin/env xxx
Shebang-Syntax.
Mit #!/usr/bin/env NAME
lässt die Shell nach der ersten Übereinstimmung von NAME in der Umgebungsvariable $PATH suchen. Es kann nützlich sein, wenn Sie den absoluten Pfad nicht kennen oder nicht danach suchen möchten.
Ausführen eines Befehls über /usr/bin/env
hat den Vorteil, nach der Standardversion des Programms in Ihrer aktuellen env zu suchen Bügeln.
Auf diese Weise müssen Sie nicht an einer bestimmten Stelle im System danach suchen, da sich diese Pfade auf verschiedenen Systemen an verschiedenen Stellen befinden können. Solange es sich in deinem Pfad befindet, wird es es finden.
Ein Nachteil ist, dass Sie nicht mehr als ein Argument übergeben können (z. B. können Sie /usr/bin/env awk -f
nicht schreiben ), wenn Sie Linux unterstützen möchten, da POSIX vage ist, wie die Zeile zu interpretieren ist, und Linux alles nach dem ersten Leerzeichen interpretiert, um ein einzelnes Argument zu bezeichnen. Sie können /usr/bin/env -S
verwenden bei einigen Versionen von env
um dies zu umgehen, aber dann wird das Skript noch weniger portabel und bricht auf ziemlich neuen Systemen (z. B. sogar Ubuntu 16.04, wenn nicht später).
Ein weiterer Nachteil ist, dass, da Sie keine explizite ausführbare Datei aufrufen, dies zu Fehlern und Sicherheitsproblemen auf Mehrbenutzersystemen führen kann (wenn es jemandem gelang, seine ausführbare Datei mit dem Namen bash
zu erhalten zum Beispiel auf deinem Weg).
#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash #gives you explicit control on a given system of what executable is called
In einigen Situationen kann die erste bevorzugt werden (wie das Ausführen von Python-Skripten mit mehreren Versionen von Python, ohne dass die ausführbare Zeile überarbeitet werden muss). Aber in Situationen, in denen Sicherheit im Mittelpunkt steht, wäre letzteres vorzuziehen, da es die Möglichkeiten der Code-Einschleusung einschränkt.