Wenn die Shell-Skripte mit #!/bin/bash
werden sie immer mit bash
de /bin
. Wenn sie jedoch mit #!/usr/bin/env bash
suchen sie nach bash
で $PATH
und beginnen dann mit dem ersten, den sie finden können.
Warum sollte dies nützlich sein? Angenommen, Sie möchten bash
Skripte, die bash 4.x oder neuer benötigen, aber Ihr System hat nur bash
3.x installiert und Ihre Distribution bietet derzeit 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 in ~/bin
zum Beispiel. Und Sie können auch Ihre $PATH
Variable in Ihrer .bash_profile
Datei zum Einbinden ~/bin
als ersten Eintrag ( PATH=$HOME/bin:$PATH
として ~
wird sich nicht ausdehnen in $PATH
). Wenn Sie nun bash
sucht die Shell zunächst in $PATH
in der Reihenfolge, d.h. sie beginnt mit ~/bin
und findet dort Ihre bash
. Das Gleiche passiert, wenn Skripte suchen nach bash
mit #!/usr/bin/env bash
so dass diese Skripte jetzt auf Ihrem System unter Verwendung Ihrer benutzerdefinierten bash
bauen.
Ein Nachteil ist, dass dies zu unerwartetem Verhalten führen kann, z.B. kann dasselbe Skript auf demselben Rechner mit verschiedenen Interpretern für verschiedene Umgebungen oder Benutzer mit verschiedenen Suchpfaden laufen, was zu allerlei Kopfschmerzen führt.
Der größte Nachteil bei env
ist, dass einige Systeme nur ein Argument zulassen, so dass Sie dies nicht tun können #!/usr/bin/env <interpreter> <arg>
wie die Systeme sehen werden <interpreter> <arg>
als ein Argument (es wird so behandelt, als ob der Ausdruck in Anführungszeichen gesetzt wäre) und somit env
sucht nach einem Interpreter namens <interpreter> <arg>
. Beachten Sie, dass es sich hierbei nicht um ein Problem des env
Befehl selbst, der immer die Übergabe mehrerer Parameter erlaubte, aber mit dem Shebang-Parser des Systems, der diese Zeile analysiert, bevor er überhaupt aufgerufen wird env
. Inzwischen wurde dies auf den meisten Systemen behoben, aber wenn Ihr Skript extrem portabel sein soll, können Sie sich nicht darauf verlassen, dass dies auf dem System, das Sie verwenden, behoben wurde.
Es kann sogar Auswirkungen auf die Sicherheit haben, z. B. wenn sudo
nicht für eine saubere Umgebung konfiguriert wurde oder $PATH
wurde von der Säuberung ausgenommen. Lassen Sie mich das demonstrieren:
Normalerweise /bin
ist ein gut geschützter Ort, nur root
in der Lage ist, dort etwas zu ändern. Ihr Home-Verzeichnis ist es jedoch nicht, jedes Programm, das Sie ausführen, kann dort Änderungen vornehmen. Das bedeutet, dass bösartiger Code ein gefälschtes bash
in ein verstecktes Verzeichnis, ändern Sie Ihre .bash_profile
um dieses Verzeichnis in Ihr $PATH
so dass alle Skripte, die #!/usr/bin/env bash
wird am Ende mit dieser Fälschung laufen bash
. Wenn sudo
hält $PATH
sind Sie in großen Schwierigkeiten.
Nehmen wir an, ein Werkzeug erstellt eine Datei ~/.evil/bash
mit dem folgenden 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 "$@"
Erstellen wir ein einfaches Skript sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Konzeptnachweis (auf einem System mit sudo
hä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
und wenn Sie sie aus irgendeinem Grund nicht dort ablegen wollen, ist es wirklich kein Problem, einen Symlink in /bin
die auf ihre tatsächlichen Standorte hinweisen (oder vielleicht /bin
selbst ein Symlink ist), daher würde ich immer mit #!/bin/sh
y #!/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 verlangen würde (POSIX standardisiert keine Pfadnamen und somit auch nicht das Shebang-Feature), aber sie sind so gebräuchlich, dass selbst wenn ein System keine /bin/sh
würde sie wahrscheinlich trotzdem verstehen #!/bin/sh
und wissen, was damit zu tun ist, und sei es nur für die Kompatibilität mit bestehendem Code.
Aber für modernere, nicht standardisierte, optionale Interpreter wie Perl, PHP, Python oder Ruby ist nirgendwo angegeben, wo sie sich befinden sollen. Sie können sein in /usr/bin
aber sie könnten genauso gut in /usr/local/bin
oder in einem ganz anderen Hierarchiezweig ( /opt/...
, /Applications/...
, usw.). Deshalb verwenden diese oft die #!/usr/bin/env xxx
Shebang-Syntax.