GNU/Linux >> LINUX-Kenntnisse >  >> Linux

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

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.


Linux
  1. /usr/bin vs. /usr/local/bin Unter Linux?

  2. Bash =~ Regex und Https://regex101.com/?

  3. Was ist der Unterschied zwischen /sbin/nologin und /bin/false?

  4. Der Unterschied zwischen /opt und /usr/local?

  5. Warum zeigt /bin/sh auf /bin/dash und nicht auf /bin/bash?

Installieren Sie Binärdateien in /bin, /sbin, /usr/bin und /usr/sbin, Interaktionen mit --prefix und DESTDIR

Was bedeuten /usr/sbin, /usr/local/sbin und /usr/local/bin?

Unterschied zwischen /bin und /usr/bin

/bin Inhalt nach /usr/bin verschoben, rückgängig machen möglich?

Sollten Websites gemäß der empfohlenen Verwendung in /var/ oder /usr/ leben?

#!/bin/sh vs. #!/bin/bash für maximale Portabilität