625 Stimmen

Was ist der Unterschied zwischen "#!/usr/bin/env bash" und "#!/usr/bin/bash"?

Was ist der Unterschied zwischen diesen beiden Anweisungen in der Kopfzeile eines Bash-Skripts?

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Als ich die env Manpage erhalte ich diese Definition:

 env - run a program in a modified environment

Was bedeutet das?

497voto

Alec Bennett Punkte 5457

Ausführen eines Befehls durch /usr/bin/env hat den Vorteil, dass es nach der Standardversion des Programms sucht, die in Ihrer aktuellen env Umwelt.

Auf diese Weise müssen Sie nicht an einer bestimmten Stelle des Systems danach suchen, da sich diese Pfade auf verschiedenen Systemen an unterschiedlichen Stellen befinden können. Solange es sich in Ihrem Pfad befindet, wird es gefunden.

Ein Nachteil ist, dass Sie nicht in der Lage sind, mehr als ein Argument zu übergeben (z. B. können Sie nicht schreiben /usr/bin/env awk -f ), wenn Sie Linux unterstützen möchten, da POSIX ist vage an, wie die Zeile zu interpretieren ist, und Linux interpretiert alles nach dem ersten Leerzeichen als ein einzelnes Argument. Sie können /usr/bin/env -S bei einigen Versionen von env um dies zu umgehen, aber dann wird das Skript noch weniger portabel und bricht auf neueren Systemen (z.B. sogar Ubuntu 16.04, wenn nicht später) ab.

Ein weiterer Nachteil ist, dass, da Sie keine explizite ausführbare Datei aufrufen, die Gefahr von Fehlern besteht und auf Mehrbenutzersystemen Sicherheitsprobleme auftreten können (wenn es jemandem gelingt, seine ausführbare Datei als bash in Ihrem Pfad, zum Beispiel).

#!/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 manchen Situationen ist die erste Variante vorzuziehen (z. B. beim Ausführen von Python-Skripten mit mehreren Python-Versionen, ohne dass die ausführbare Zeile überarbeitet werden muss). Aber in Situationen, in denen die Sicherheit im Vordergrund steht, wäre letztere vorzuziehen, da sie die Möglichkeiten zur Code-Injektion einschränkt.

117voto

Dolphiniac Punkte 1632

Verwendung von #!/usr/bin/env NAME lässt die Shell nach der ersten Übereinstimmung von NAME in der Umgebungsvariablen $PATH suchen. Dies kann nützlich sein, wenn Sie den absoluten Pfad nicht kennen oder nicht danach suchen wollen.

64voto

Mecki Punkte 113876

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.

16voto

safay Punkte 469

Anstatt den Pfad zum Interpreter explizit zu definieren, wie in /usr/bin/bash/ Wenn Sie den Befehl env verwenden, wird der Interpreter dort gesucht und gestartet, wo er zuerst gefunden wird. Dies hat sowohl Vor- und Nachteile

4voto

sudo97 Punkte 298

Ich finde es nützlich, denn als ich noch nichts von env wusste, bevor ich anfing, ein Drehbuch zu schreiben, habe ich dies getan:

type nodejs > scriptname.js #or any other environment

und dann habe ich diese Zeile in der Datei in shebang geändert.
Ich habe das gemacht, weil ich mich nicht immer daran erinnern konnte, wo sich nodejs auf meinem Computer befindet -- /usr/bin/ oder /bin/, also für mich env ist sehr nützlich. Vielleicht gibt es Details dazu, aber das ist mein Grund

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X