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

372

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

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Wenn ich die env Manpage konsultiere , erhalte ich folgende Definition:

 env - run a program in a modified environment

Was bedeutet das?

Tarrsalah
quelle
7
Siehe diese Frage und meine Antwort .
Keith Thompson
6
Wer kann mir sagen, warum diese Frage "im Zusammenhang mit Programmierung oder Softwareentwicklung" nicht geschlossen ist?
Tarrsalah
1
Ich bin damit einverstanden, dass es nicht zum Thema gehört, aber es ist wahrscheinlich ein Duplikat mehrerer anderer Fragen wie dieser .
Keith Thompson
16
Diese Frage sollte nicht als nicht zum Thema gehörend markiert worden sein. Es werden nur 5 Personen mit einer Punktzahl von über 3000 benötigt, um es als "thematisch" zu markieren, und es kann wieder geöffnet werden. Es ist eine Frage - speziell zur Programmierung.
Danijel-James W
3
Ich bin geschockt. Schockiert, dass die Linux-Dokumentation voller Tautologien ist. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Antworten:

323

Einen Befehl durch Ausführen /usr/bin/envhat 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 -Seinige Versionen von verwenden env, 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 bashin Ihrem Pfad aufzurufen ).

#!/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 (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.

Alec Bennett
quelle
22
Ein weiterer Nachteil ist, dass Sie dem Interpreter kein zusätzliches Argument übergeben können.
Keith Thompson
1
@KeithThompson: Falsche Informationen. Sie können Optionen mit / usr / bin / env an den zugrunde liegenden Interpreter übergeben!
Gaurav Agarwal
4
@ GauravAgarwal: Nicht auf meinem System. Ein Skript, das nur diese eine Zeile enthält: #!/usr/bin/env echo Hellobeschwert sich : /usr/bin/env: echo Hello: No such file or directory. Anscheinend wird es echo Helloals ein einziges Argument behandelt /usr/bin/env.
Keith Thompson
1
@ AndréLaszlo: Mit dem envBefehl 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.
Keith Thompson
1
Warum gibt es Backticks im Codeblock? Sollten sie nicht entfernt werden?
Benjamin W.
64

Mit using #!/usr/bin/env NAMEsucht 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.

Delphiniac
quelle
9
Zumindest muss man wissen, wo env ist :).
Devrimbaris 22.
1
Hervorragende Antwort. Erklärt kurz und bündig, was der env shebang tut, anstatt zu sagen "wählt das Programm basierend auf Ihrer Systemkonfiguration aus"
De Novo
13

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 Nachteile

Safay
quelle
Auf den meisten Systemen sind sie funktional identisch, dies hängt jedoch vom Speicherort Ihrer ausführbaren Bash- und Env-Dateien ab. Ich bin mir jedoch nicht sicher, wie sich dies auf Umgebungsvariablen auswirkt.
Safay
2
"Es ist möglich, den Interpreter ohne Verwendung von env anzugeben, indem der vollständige Pfad zum Interpreter angegeben wird. Ein Problem besteht darin, dass auf verschiedenen Computersystemen der genaue Pfad unterschiedlich sein kann. Durch die Verwendung von env wird der Interpreter gesucht und gefunden Dies macht das Skript portabler, erhöht aber auch das Risiko, dass der falsche Interpreter ausgewählt wird, da er in jedem Verzeichnis auf dem ausführbaren Suchpfad nach einer Übereinstimmung sucht. Das gleiche Problem besteht auch darin, dass das Skript ausgeführt wird Der Pfad zur env-Binärdatei kann auch pro Maschine unterschiedlich sein. "- Wikipedia
Mike Clark
10

Wenn die Shell-Skripte mit beginnen #!/bin/bash, werden sie immer mit bashvon ausgeführt /bin. Wenn sie jedoch mit beginnen #!/usr/bin/env bash, suchen sie nach bashin $PATHund beginnen dann mit dem ersten, den sie finden können.

Warum sollte das nützlich sein? Angenommen, Sie möchten bashSkripts ausführen , für die bash 4.x oder höher erforderlich ist, auf Ihrem System jedoch nur bash3.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 ~/binbeispielsweise platzieren. Sie können Ihre $PATHVariable auch so ändern .bash_profile, dass sie ~/binals erster Eintrag enthalten ist ( PATH=$HOME/bin:$PATHda dies ~nicht erweitert wird $PATH). Wenn Sie jetzt anrufen bash, sucht die Shell zuerst in der richtigen $PATHReihenfolge danach, also beginnt sie damit ~/bin, wo sie Ihre findet bash. Dasselbe passiert, wenn Skripte nach bashVerwendung suchen #!/usr/bin/env bash, sodass diese Skripte jetzt mit Ihrem benutzerdefinierten bashBuild 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 envist, 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 daher envnach einem benannten Interpreter suchen <interpreter> <arg>. Beachten Sie, dass dies kein Problem des envBefehls selbst ist, bei dem immer mehrere Parameter übergeben werden konnten, sondern mit dem Shebang-Parser des Systems, das diese Zeile vor dem Aufruf analysiert env. 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 sudoes nicht für die Bereinigung der Umgebung konfiguriert oder $PATHvon der Bereinigung ausgeschlossen wurde. Lassen Sie mich dies demonstrieren:

In der Regel /binist ein gut geschützter Ort, nur rootkann 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älschung bashin ein verstecktes Verzeichnis einfügen kann. Ändern Sie Ihr Verzeichnis so, .bash_profiledass dieses Verzeichnis in Ihr Verzeichnis aufgenommen wird $PATH, sodass alle verwendeten Skripte #!/usr/bin/env bashmit dieser Fälschung ausgeführt werden bash. Wenn Sie sudobehalten $PATH, sind Sie in großen Schwierigkeiten.

Angenommen, ein Tool erstellt eine Datei ~/.evil/bashmit 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 "$@"

Lassen Sie uns ein einfaches Skript erstellen sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Proof of Concept (auf einem System, auf dem sudoaufbewahrt wird $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 befinden. /binWenn 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 /binselbst ein Symlink ist) Ich würde immer mit #!/bin/shund 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/shwürde, es wahrscheinlich immer noch verstehen würde #!/bin/shund 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/binaber sie können auch in sein /usr/local/binoder in einem ganz anderen Hierarchiezweig ( /opt/..., /Applications/..., etc.). Deshalb verwenden diese oft die #!/usr/bin/env xxxShebang-Syntax.

Mecki
quelle
4

Ich finde es nützlich, denn als ich nichts über env wusste, bevor ich anfing, ein Skript zu schreiben, tat ich Folgendes:

type nodejs > scriptname.js #or any other environment

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 envist dies für mich sehr nützlich. Vielleicht gibt es Details dazu, aber das ist mein Grund

sudo97
quelle