Warum ist #! / Usr / bin / env bash #! / Bin / bash überlegen?

212

Ich habe an einer Reihe von Stellen gesehen, einschließlich Empfehlungen auf dieser Website ( Was ist der bevorzugte Bash Shebang? ), Um#!/usr/bin/env bash bevorzugt zu verwenden #!/bin/bash. Ich habe sogar ein unternehmender Einzel gesehen empfehlen die Verwendung #!/bin/bashwar falsch und bash Funktionalität würde , indem Sie so verloren.

Trotzdem verwende ich bash in einer streng kontrollierten Testumgebung, in der jedes im Umlauf befindliche Laufwerk im Wesentlichen ein Klon eines einzelnen Master-Laufwerks ist. Ich verstehe das Portabilitätsargument, obwohl es in meinem Fall nicht unbedingt anwendbar ist. Gibt es einen anderen Grund, #!/usr/bin/env bashden Alternativen den Vorzug zu geben, und gibt es einen Grund, der die Funktionalität beeinträchtigen könnte, wenn die Portabilität ein Problem darstellt?

spugm1r3
quelle
7
Es ist nicht unbedingt besser. Siehe diese Frage und meine Antwort auf unix.stackexchange.com. (Ich würde dafür stimmen, dies als Duplikat zu schließen, aber ich glaube nicht, dass Sie das über Websites hinweg tun können.)
Keith Thompson
2
Zusätzlich zur Antwort von @ zigg befindet sich envmöglicherweise nicht bei /usr/bin. Shebang Kommentare sind meiner Meinung nach insgesamt eine schlechte Idee. Wenn Ihr Standard-Skriptinterpreter keine Shebang-Kommentare verarbeitet, handelt es sich nur um einen Kommentar. Wenn Sie jedoch wissen, dass der Skriptinterpreter mit Shebang-Kommentaren umgehen kann, und Sie den Pfad zum Bash kennen, gibt es keinen Grund, ihn nicht über seinen absoluten Pfad aufzurufen, es sei denn, der Pfad ist zu lang (unwahrscheinlich), oder Sie portieren das Skript möglicherweise zu einem System, in dem sich keine Bash in / bin befindet. Andererseits gelten in diesem Fall die zuvor erwähnten Einschränkungen, da es sich um Portabilität handelt.
1
@ KeithThompson, danke für den Link. Vielleicht war meine Suche nach einer Antwort vor dem Posten der Frage etwas eng. Meine Abkehr von all dem: (1) Linux / Unix / Posix / etc ... ist grau, und (2) jeder, der behauptet, absolut die richtige Antwort zu haben, hat absolut die richtige Antwort für sein spezielles Szenario.
spugm1r3
3
Das Verhalten vieler Dinge in POSIX / Unix ist gut definiert. Die Standorte sind nicht immer so eindeutig. Etwas muss wie /etcoder existieren /bin/sh. bashist ein Add-On für die meisten Unix-ähnlichen Systeme. Es ist nur Linux, wo bashes garantiert ist /binund höchstwahrscheinlich auch als /bin/sh. Seit Linux für viele Menschen das moderne De-facto-Unix wurde, wurde die Tatsache vergessen, dass andere Systeme als Linux existieren könnten. In meiner eigenen Antwort unten habe ich Linux angenommen, weil Sie gesagt haben bash. Viele der BSD-Boxen, mit denen ich gearbeitet habe, hatten es nicht einmal installiert.
Sean Perry
2
@Keith - Im Fall von Bash (im Gegensatz zu Python in den anderen Fragen) ... OpenBSD hat keine /bin/bash. Bash ist nicht standardmäßig installiert. Wenn du es willst, musst du pkg install bash. Einmal installiert, befindet es sich bei /usr/local/bin/bash. Auf /bin/bashOpenBSD ist nichts installiert . Ein Scheiß auf #!/bin/bashWillensfehler und #!/usr/bin/env bashErfolg.
JWW

Antworten:

229

#!/usr/bin/envsucht PATHnach bashund bashist nicht immer in /bin, insbesondere auf Nicht-Linux-Systemen. Auf meinem OpenBSD-System ist es beispielsweise in /usr/local/bin, da es als optionales Paket installiert wurde.

Wenn Sie absolut sicher bashsind /bin, dass dies der Fall ist und es immer sein wird, kann es nicht schaden, es direkt in Ihren Shebang zu setzen - aber ich würde es ablehnen, da Skripte und Programme alle ein Leben haben, das über das hinausgeht, was wir ursprünglich angenommen haben.

Zickzack
quelle
29
Was ist mit der envLage? POSIX erzwingt es nicht.
Julio Guerra
1
@JulioGuerra Ähnlich wie eine /usr/lib/sendmail(oder in jüngerer Zeit /usr/sbin/sendmail) Binärdatei für die Verarbeitung von E-Mails verfügbar ist, liegt dies im Interesse eines Unix-ähnlichen Systems, /usr/bin/envda der env shebang eine so gängige Praxis ist. Es ist eine De-facto-Standardschnittstelle.
Zigg
8
@zigg Das ist so UN * X ... <-: Ich meine, es ist in ihrem besten Interesse, einen Standardstandort für zu haben env, aber irgendwie keinen Standardstandort (der nur ein Softlink sein kann) für bash. Ganz zu schweigen davon, warum dann Hashbang nicht einfach akzeptiert #!bashund das verwendet PATH, anstatt dass wir genau das Gleiche mit tun env. Nicht verwirrend genug für Anfänger, denke ich.
Ddekany
1
@ JulioGuerra Laut Wikipedia sind Sie wahr: Es ist nicht "garantiert". Stattdessen scheint es "wahrscheinlicher". Wikipedia sagt This mostly works because the path /usr/bin/env is commonly used for the env utilityhier en.wikipedia.org/wiki/Shebang_(Unix) - Wir müssen uns darauf verlassen env, dass wir "wahrscheinlich" in allen Systemen da sind.
Xavi Montero
2
@XaviMontero: Ich habe ein System verwendet, in dem es sich envbefand /bin, nicht in /usr/bin(nicht sicher, welches, wahrscheinlich SunOS 4). Heutzutage wird es sehr wahrscheinlich /usr/bin/envverfügbar sein, nur wegen der Popularität des #!/usr/bin/envHacks.
Keith Thompson
39

Der Standardspeicherort für Bash ist /bin, und ich vermute, dass dies auf allen Systemen zutrifft. Was ist jedoch, wenn Ihnen diese Version von Bash nicht gefällt? Zum Beispiel möchte ich Bash 4.2 verwenden, aber der Bash auf meinem Mac ist bei 3.2.5.

Ich könnte versuchen, bash neu zu installieren, /binaber das könnte eine schlechte Idee sein. Wenn ich mein Betriebssystem aktualisiere, wird es überschrieben.

Ich könnte jedoch bash in installieren /usr/local/bin/bashund meinen PATH wie folgt einrichten:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

Wenn ich jetzt spezifiziere bash, bekomme ich nicht den alten /bin/bash, sondern den neueren, glänzenderen /usr/local/bin. Nett!

Außer meine Shell-Skripte haben diesen !# /bin/bashSchebang. Wenn ich also meine Shell-Skripte ausführe, erhalte ich diese alte und miese Version von Bash, die nicht einmal assoziative Arrays enthält.

Mit /usr/bin/env bashwird die in meinem PATH gefundene Bash-Version verwendet. Wenn ich meinen PATH so einrichte, dass er /usr/local/bin/bashausgeführt wird, ist dies die Bash, die meine Skripte verwenden.

Es ist selten, dies bei Bash zu sehen, aber es ist viel häufiger bei Perl und Python:

  • Bestimmte Unix / Linux-Versionen, die sich auf Stabilität konzentrieren, liegen mit der Veröffentlichung dieser beiden Skriptsprachen manchmal weit zurück. Vor nicht allzu langer Zeit war RHELs Perl bei 5.8.8 - eine acht Jahre alte Version von Perl! Wenn jemand modernere Funktionen verwenden wollte, musste er seine eigene Version installieren.
  • Mit Programmen wie Perlbrew und Pythonbrew können Sie mehrere Versionen dieser Sprachen installieren. Sie hängen von Skripten ab, die Ihren PFAD manipulieren, um die gewünschte Version zu erhalten. Wenn Sie den Pfad hart codieren, kann ich mein Skript nicht unter Brew ausführen .
  • Es ist noch nicht so lange her (okay, es ist lange her), dass Perl und Python keine Standardpakete waren, die in den meisten Unix-Systemen enthalten waren. Das bedeutete, dass Sie nicht wussten, wo diese beiden Programme installiert waren. War es unter /bin? /usr/bin? /opt/bin? Wer weiß? Verwenden #! /usr/bin/env perlbedeutete, dass ich es nicht wissen musste.

Und jetzt, warum Sie nicht verwenden sollten #! /usr/bin/env bash

Wenn der Pfad im Shebang fest codiert ist, muss ich mit diesem Dolmetscher laufen. Daher #! /bin/bashzwinge ich mich, die standardmäßig installierte Version von bash zu verwenden. Da die Bash-Funktionen sehr stabil sind (versuchen Sie, eine 2.x-Version eines Python-Skripts unter Python 3.x auszuführen), ist es sehr unwahrscheinlich, dass mein spezielles BASH-Skript nicht funktioniert, und da mein Bash-Skript wahrscheinlich von diesem System und anderen Systemen verwendet wird Die Verwendung einer nicht standardmäßigen Version von Bash kann unerwünschte Auswirkungen haben. Es ist sehr wahrscheinlich, dass ich sicherstellen möchte, dass die stabile Standardversion von bash mit meinem Shell-Skript verwendet wird. Daher möchte ich wahrscheinlich den Pfad in meinem Shebang hart codieren.

David W.
quelle
5
Dies ist nicht wahr: "Der Standardspeicherort von bash ist / bin" (es sei denn, Sie können ein Standarddokument zitieren). Es ist vielleicht genauer, dass es der "übliche" Speicherort auf den meisten Linux-Distributionen und Macos ist, aber es ist kein Standard auf Unix-Systemen im Allgemeinen (und ist insbesondere nicht der Speicherort auf den meisten * bsds).
tesch1
13

Zum Aufrufen ist bashes ein bisschen übertrieben. Es sei denn, Sie haben mehrere bashBinärdateien wie Ihre eigene in ~ / bin, aber das bedeutet auch, dass Ihr Code davon abhängt, dass $ PATH die richtigen Dinge enthält.

Es ist praktisch für Dinge wie python. Es gibt Wrapper-Skripte und -Umgebungen, die dazu führen, dass alternative pythonBinärdateien verwendet werden.

Wenn Sie jedoch den genauen Pfad zur Binärdatei verwenden, geht nichts verloren, solange Sie sicher sind, dass es sich um die Binärdatei handelt, die Sie wirklich möchten.

Sean Perry
quelle
Genau richtig für python. Ich habe die Anzahl der Plätze verloren, pythondie gefunden werden können 😀
zigg
2
Einverstanden, dass /usr/bin/envdies für Python nützlicher ist, insbesondere wenn Sie virtualenv verwenden.
Dennis
10

Es gibt viele Systeme ohne Bash /bin, FreeBSD und OpenBSD, um nur einige zu nennen. Wenn Ihr Skript auf viele verschiedene Unices portierbar sein soll, möchten Sie es möglicherweise #!/usr/bin/env bashanstelle von verwenden #!/bin/bash.

Beachten Sie, dass dies nicht gilt für sh; für Bourne-kompatible Skripte ausschließlich verwende ich #!/bin/sh, da ich in Existenz so ziemlich jedes Unix denken hat shin /bin.

James Ko
quelle
In Ubuntu 18.04 /bindir sehe ich sh -> dash. Symbolisch verbunden mit dashUbuntus Debian-Natur. Führen Sie diese drei Befehlszeichenfolgen aus, um zu erkennen, dass alles auf die individuellen Vorlieben hinausläuft: which bashdann which shdann which dash.
Noobninja
0

Ich würde es vorziehen, das Hauptprogramm in ein Skript wie das folgende zu packen, um alle bashauf dem System verfügbaren zu überprüfen . Besser mehr Kontrolle über die verwendete Version.

#! /usr/bin/env bash

# This script just chooses the appropriate bash
# installed in system and executes testcode.main

readonly DESIRED_VERSION="5"

declare all_bash_installed_on_this_system
declare bash

if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
    found=0

    all_bash_installed_on_this_system="$(\
        awk -F'/' '$NF == "bash"{print}' "/etc/shells"\
        )"

    for bash in $all_bash_installed_on_this_system
    do
        versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
        [ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
    done
    if [ "${found}" -ne 1 ]
    then
        echo "${DESIRED_VERSION} not available"
        exit 1
    fi
fi

$bash main_program "$@"
Mihir
quelle
0
 #!/usr/bin/env bash

ist definitiv besser, weil es den ausführbaren Pfad von bash aus Ihrer Systemumgebungsvariablen findet.

Gehen Sie zu Ihrer Linux-Shell und geben Sie ein

env

Es werden alle Ihre Umgebungsvariablen gedruckt.

Gehen Sie zu Ihrem Shell-Skript und geben Sie ein

echo $BASH

Es wird Ihr Bash-Pfad (gemäß der Liste der Umgebungsvariablen) gedruckt, den Sie verwenden sollten, um Ihren korrekten Shebang-Pfad in Ihrem Skript zu erstellen.

kta
quelle