Standard Bash 3 überschreibt shebang /bin/bash mit brew installiertem bash 4

Hintergrund

Bash 4 wurde erfolgreich mit Homebrew und den zusätzlichen Schritten installiert, die erforderlich sind, um Bash 4 zu Ihrer Standard-Shell zu machen. Viele Anleitungen im Netz mit einfachem Googlen: Beispiel Bash 4 Upgrade Guide für Mac

  • Bash 4.x (über Brew installiert) Speicherort:/usr/local/bin/bash
  • Speicherort von Bash 3.2 (Standard OSX):/bin/bash

Problem

Skripte Ich kann den Shebang nicht steuern/modifizieren , da dies andere Benutzer beeinträchtigen würde. Es sei denn, ich musste manuell ändern und dann wiederherstellen, bevor ich mich auf die Quellcodeverwaltung festlegte. Eine andere Möglichkeit besteht darin, Folgendes einzugeben:bash some_script_with_bin_bash_shebang.shum die Verwendung der neu standardmäßigen bash 4-Shell zu erzwingen.

Ich muss dies immer nur für die Bash 4+-Funktionalität tun, die jetzt sehr verbreitet ist.

Frage

Hat jemand eine coole Technik, Problemumgehungen, um einen Shebang-Bash-Pfad wie folgt zu aliasieren: #!/bin/bashzur neueren Bash 4-Shell unter /usr/local/bin/bash?

Ohne offensichtlich die Datei zu verändern! ^_^ (z. B. Hinzufügen von #!/usr/bin/env bash)

Antworten (3)

Dies ist heutzutage der Hauptgrund für die Verwendung eines anderen Krams:

#!/usr/bin/env bash

Dies wird die erste Bash im PATH verwenden, die höchstwahrscheinlich die neueste Version sein wird. Da sich /bin im Standardpfad ( getconf PATH) befindet, fällt dies für diejenigen ohne benutzerdefinierte Bash problemlos zurück.

Ich glaube du hast meine Frage nicht wirklich gelesen? Das ist mir schon bekannt, aber das beantwortet meine Frage überhaupt nicht. Gibt es etwas, was ich tun sollte, um dies klarer zu machen? Was ich nicht weiß, ist die Möglichkeit dessen, was ich gefragt habe lol, ich verstehe, dass Sie versuchen, die einfachen Punkte zu bekommen, aber ich habe gesagt, ohne die Datei zu ändern. Um solche Reaktionen zu vermeiden.
@RST Ich habe deine Frage gelesen, aber es scheint ein XY-Problem zu sein, und ich habe versucht, es wieder auf Kurs zu bringen. Verwenden Sie für Ihre eigenen Skripte, die die 'Bash 4+-Funktionalität' verwenden, env bash, und für Skripte, die Sie nicht geschrieben haben, lassen Sie sie in Ruhe! Wenn ich ein Skript zum Verteilen auf Mac schreibe und /bin/bash verwende, dann deshalb, weil ich die System-Bash verwenden möchte, und die Verwendung dieses Shebangs stellt sicher, dass dies geschieht. Wenn ich die neueste verfügbare Version von bash verwenden möchte, ist env bash überlegen, aber das bedeutet nicht, dass ich andere Skripte, die ich nicht geschrieben habe, dazu zwingen sollte, das zu verwenden, was ich für das Beste halte.
So wie einige Benutzer auf Linux und andere auf Mac arbeiten, haben 99 % der Linux-Benutzer seit Jahren die neueste Bash und den env-basierten Ansatz. Im Grunde ja, der beste Fall wäre, sie alle zu ändern, das ist im Moment keine Möglichkeit. Deshalb habe ich aus Interesse noch einmal gesagt, ohne die Datei zu ändern. Ich stimme nicht unbedingt mit allem überein, was Sie sagen, aber wie das Leben ist, hah.
@RST Wörtlich genommen lautet Ihre Frage: "Warum öffnen Skripte, die die Datei öffnen sollen, /bin/bashdie Datei /bin/bashund nicht eine völlig andere Datei /usr/local/bin/bash? Die logisch korrekte Antwort lautet: "Weil das Skript dazu aufgefordert wurde, sollten Sie das Skript bearbeiten wenn du willst, dass es etwas anderes tut."

Erstens könnten Sie erwägen, einfach /bin/bashdurch eine aktuellere Version zu ersetzen.

Zweitens, wenn Sie damit Ihre Garantie nicht aufheben möchten und bereits ein Problem mit einem vorhandenen Skript haben, umgehen Sie dessen #!Zeile, indem Sie es wie folgt aufrufen:

/path/to/bash4/path/to/script.bashargs...

(Sie können einfach bashanstelle des vollständigen Pfads verwenden, wenn Sie $PATHin der richtigen Reihenfolge sind.)

Drittens, wenn dieses Skript spezifisch für Apple-Geräte ist und Bash-Version 4 haben muss, dann setzen Sie es immer explizit auf #!/path/to/bash4, da es keinen Vorteil bringt, es auf etwas anderes einzustellen. (Dies bedeutet nicht , dass Sie es in Ihrem Quell-Repository ändern, sondern es während der Installation auswählen; siehe unten.)

Viertens sollten Sie die Verwendung von https://gist.github.com/kurahaupo/8130030 in Erwägung ziehen – beachten Sie, dass die „Quell“-Version des Skripts außerhalb des beabsichtigten binVerzeichnisses liegen muss; Lassen Sie es zB in Ihrem Quell-Repo oder in einem temporären Verzeichnis (das nach dieser "Installation" gelöscht werden kann).

Fünftens, wenn Sie das ursprüngliche Skript wirklich nicht anfassen möchten, erstellen Sie ein Stub-Skript, das es aufruft:

#!/bin/sh
exec /path/to/bash4 /path/where/you/stashed/the/original/script "$@"

und installieren Sie diese in einem geeigneten binVerzeichnis.

Quelle vs. Bereitstellung

Ihre Frage zum Festlegen der #!Zeile im Quellrepo unterstreicht ein weit verbreitetes Missverständnis über Skripte: die Idee, dass ein Skript, nur weil es "Text" ist, nicht in irgendeiner Weise "für die Verwendung vorbereitet" werden sollte.

Für andere Arten von Programmen ist es offensichtlich: Sie müssen kompiliert werden. Und wenn es sich um große Python- oder Perl-Skripte handelt, benötigen sie ein Paket oder Installationsprogramm, das alle Modulabhängigkeiten enthält.

Aber selbst ein Shell-Skript muss zumindest seinen Besitz und seine Berechtigungen haben und in einem binVerzeichnis abgelegt werden. Normalerweise ist dies entweder der Fall, wenn ein Paket für eine Geräteklasse erstellt wird, oder wenn das Skript auf einem Zielgerät installiert wird. Dies ist auch der geeignete Zeitpunkt, um die Leitung passend zur Einsatzumgebung einzustellen .#!

Ich empfehle nicht#!/usr/bin/env bash

Die weitverbreitete Werbung für #!/usr/bin/envetwas schwächt die allgemeine Sicherheit, aber auf eine Art und Weise, die wahrscheinlich nicht die Leute, die es schreiben, beißt, sondern nur die Benutzer irgendwo im weiteren Verlauf. Daher empfehle ich es nicht ; Tatsächlich sage ich voraus, dass es irgendwann zurückkommen und Ihre Kunden beißen wird, oder die Kunden Ihrer Kunden, oder die Kunden Ihrer Kunden ... Kunden.

Es wird als "tragbar" beworben, aber das stimmt nicht wirklich: Android- und OpenWRT-Geräte haben nicht einmal /usrviel weniger /usr/bin/env, also ist es dort ziemlich nutzlos. In der Praxis sind die Hauptnutznießer also Apple-Benutzer.

Wenn das Skript eine neue Version von bash erfordert, reicht die Systemversion einfach nicht aus, daher ist es sinnlos, sie als Fallback zu verwenden. Wenn Sie jedoch nicht die neueste Version benötigen, ist es in Ordnung, sie als /bin/bash zu fixieren.

Kurz gesagt, wenn Sie ein Skript für Apple bereitstellen, das Bash Version 4 erfordert, sollten Sie verwenden #!/path/to/bash4, entweder wenn Sie das Skript in ein installierbares Paket einbauen oder wenn das Skript auf einem bestimmten Gerät installiert wird.

BEARBEITEN

Ich habe es /usr/local/bin/bashdurch ersetzt /path/to/bash4, weil das etwas Verwirrung gestiftet hat. Der Punkt ist nicht, dass /usr/local/bin/bashverwendet werden muss, sondern dass der vollständige Pfad zu bash4, was auch immer es ist, explizit angegeben werden sollte. Wenn Sie es vor der Installation nicht wissen, ist es sinnvoll, $PATH dort nachzusehen, aber Sie möchten einen kurzen Test wie z

bash_path=$( type -p bash )
if ! "$bash_path" -c '[[ $BASH_VERSION > 4.2 ]]'
then
    echo "ERROR: $bash_path isn't new enough"
    exit 1
fi
{
  printf '#!%s\n' "$bash_path"
  tail -n+2 "$downloaded_file"
} > "$installpath"
chmod a=rx "$installpath"
Bash wird jedoch nicht immer in /usr/local/bin installiert, es hängt von Ihrem Paketsystem ab - weshalb der env-Weg nützlich ist
Es scheint, dass das Ersetzen von /bin/bash explizit vom Betriebssystem blockiert wird: sudo mv -vf /bin/bash /bin/bash_oldführt zumv: cannot move '/bin/bash' to '/bin/bash_old': Operation not permitted
+1 für den Hinweis, dass die Verwendung von Shell-Skripten auf Plattformen mit unterschiedlichen Standards die Bereitstellung wie jede andere Anwendung erfordert.

Meine Problemumgehung besteht einfach darin, den Dolmetscher direkt anzurufen.

Also zum Beispiel, anstatt so etwas wie:

./foo.sh

Ich nenne es stattdessen mit:

bash ./foo.sh

Die erste Form (die './foo.sh' aufruft) übt den in der Shebang-Zeile definierten Interpreter aus und gibt Ihnen (unerwünschtes) Bash 3.x auf OSX (zumindest ab OSX Big Sur). Die zweite Form ignoriert jedoch die Shebang-Zeile und ruft die Version von bash auf, die Sie in Ihrem PATH haben. Wenn Ihr PATH „/usr/local/bin“ (oder wo auch immer Sie mit Brew installieren) vor „/bin“ vorkommt, führen Sie Ihr Skript mit der gewünschten Bash-Version aus, unabhängig davon, was in der Shebang-Zeile steht. Daher können Sie Ihr Skript mit Goodies ausführen, die bash 4.x+ benötigen (wie assoziative Arrays).

Dadurch wird das Ziel erreicht, das Skript überhaupt nicht ändern zu müssen, während es dennoch mit der gewünschten Version von bash ausgeführt wird. Zum jetzigen Zeitpunkt können Sie bash 5.x mit Brew erhalten, während OSX 11.2 „Big Sur“ immer noch mit /bin/bash in v3.x ausgeliefert wird.

Diese Methode passt am besten, wenn Sie für Plattformen entwickeln , auf denen „/bin/bash“ zuverlässig die gewünschte Version ist, Sie aber auf OSX entwickeln, auf dem „/bin/bash“ 3.x ist. Auf diese Weise können Sie mit der beabsichtigten Zielversion von bash entwickeln und testen, ohne die Shebang-Zeile des Skripts auf eine Weise zu ändern, die es auf Zielplattformen weniger zuverlässig macht. Insbesondere für Bash ziehe ich es auch vor, den #!/usr/bin/env-Trick nicht zu verwenden, da dies die Komplexität für Benutzer erhöht und die Komplexität der Unterstützung erhöhen kann.

Ich verstehe den Punkt, der zu Source vs. Deployment angesprochen wurde, und dass es keine absolute Anforderung gibt, Deployment-Änderungen an Shell-Skripten nicht zuzulassen. Es kann jedoch eine große Sache sein, die Shebang-Zeilen in der Versionskontrolle ändern zu dürfen und dies dazu zu führen, dass Änderungen in den Bereitstellungsprozessen erforderlich sind, und Sie haben möglicherweise keine Kontrolle darüber. Das Vorhandensein von Shell-Skripten, die einen Bereitstellungsprozess erfordern, der die Prüfsumme des ursprünglichen Codes ändert, verringert einen der wichtigsten „Einfachheits“-Vorteile der Verwendung von Bash-Shell-Skripten. Wenn Skripte als Teil eines größeren Produkts bereitgestellt werden, das sicherstellen kann, dass komplexe Installationsschritte zuverlässig ausgeführt werden, würde ich nichts dagegen einwenden. Aber wenn Skripte das Produkt sind, hat es einen großen Vorteil, die Komplexität der Bereitstellung auf „Entpacken aus einem Tarball“ zu beschränken.