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

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

Lösung 1:

Es gibt ungefähr vier Ebenen der Portabilität für Shell-Skripte (was die Shebang-Zeile betrifft):

  1. Am portabelsten:Verwenden Sie einen #!/bin/sh shebang und nur verwenden die im POSIX-Standard spezifizierte grundlegende Shell-Syntax. Dies sollte auf so ziemlich jedem POSIX/Unix/Linux-System funktionieren. (Nun, außer Solaris 10 und früher, das die echte alte Bourne-Shell hatte, die älter als POSIX war, also nicht konform, als /bin/sh .)

  2. Zweittragbarste:Verwenden Sie eine #!/bin/bash (oder #!/usr/bin/env bash ) Shebang-Linie und bleibe bei bash v3-Funktionen. Dies funktioniert auf jedem System, das Bash hat (am erwarteten Ort).

  3. Drittportabelste:Verwenden Sie einen #!/bin/bash (oder #!/usr/bin/env bash ) Shebang-Zeile und verwenden Sie bash v4-Funktionen. Dies schlägt auf jedem System mit Bash v3 fehl (z. B. macOS, das es aus Lizenzgründen verwenden muss).

  4. Am wenigsten portierbar:Verwenden Sie #!/bin/sh shebang und verwenden Sie Bash-Erweiterungen für die POSIX-Shell-Syntax. Dies schlägt auf jedem System fehl, das etwas anderes als bash für /bin/sh hat (z. B. aktuelle Ubuntu-Versionen). Tun Sie dies niemals; Es ist nicht nur ein Kompatibilitätsproblem, es ist einfach falsch. Leider ist es ein Fehler, den viele Leute machen.

Meine Empfehlung:Verwenden Sie die konservativste der ersten drei, die alle Shell-Funktionen bereitstellt, die Sie für das Skript benötigen. Verwenden Sie für maximale Portabilität Option Nr. 1, aber meiner Erfahrung nach sind einige Bash-Funktionen (wie Arrays) so hilfreich, dass ich mich für Nr. 2 entscheiden werde.

Das Schlimmste, was Sie tun können, ist # 4, den falschen Kram zu verwenden. Wenn Sie sich nicht sicher sind, welche Funktionen grundlegende POSIX- und welche Bash-Erweiterungen sind, bleiben Sie entweder bei einem Bash-Shebang (z. B. Option Nr. 2) oder testen Sie das Skript gründlich mit einer sehr einfachen Shell (wie dash auf Ihren Ubuntu LTS-Servern). Das Ubuntu-Wiki hat eine gute Liste von Bashismen, auf die man achten sollte.

Es gibt einige sehr gute Informationen über die Geschichte und die Unterschiede zwischen Shells in der Unix- und Linux-Frage "Was bedeutet es, sh-kompatibel zu sein?" und die Stackoverflow-Frage "Unterschied zwischen sh und bash".

Beachten Sie auch, dass die Shell nicht das einzige ist, was sich zwischen verschiedenen Systemen unterscheidet; Wenn Sie an Linux gewöhnt sind, sind Sie an die GNU-Befehle gewöhnt, die viele nicht standardmäßige Erweiterungen haben, die Sie möglicherweise auf anderen Unix-Systemen (z. B. BSD, MacOS) nicht finden. Leider gibt es hier keine einfache Regel, Sie müssen nur den Variationsbereich für die von Ihnen verwendeten Befehle kennen.

Einer der unangenehmsten Befehle in Bezug auf Portabilität ist einer der einfachsten:echo . Jedes Mal, wenn Sie es mit beliebigen Optionen verwenden (z. B. echo -n oder echo -e ) oder mit irgendwelchen Escapezeichen (Backslashes) in der zu druckenden Zeichenfolge, werden verschiedene Versionen unterschiedliche Dinge tun. Jedes Mal, wenn Sie einen String ohne Zeilenvorschub danach oder mit Escapezeichen im String ausgeben möchten, verwenden Sie printf stattdessen (und erfahren Sie, wie es funktioniert - es ist komplizierter als echo ist). Die ps Befehl ist auch ein Durcheinander.

Eine weitere allgemeine Sache, auf die man achten sollte, sind neuere/GNU-artige Erweiterungen der Befehlsoptionssyntax:Das alte (Standard-)Befehlsformat besteht darin, dass dem Befehl Optionen folgen (mit einem einzelnen Bindestrich, und jede Option ist ein einzelner Buchstabe), gefolgt von Befehlsargumente. Neuere (und oft nicht portable) Varianten enthalten lange Optionen (normalerweise mit -- eingeleitet ), das Zulassen von Optionen nach Argumenten und die Verwendung von -- um Optionen von Argumenten zu trennen.

Lösung 2:

Im ./configure Skript, das die TXR-Sprache zum Bauen vorbereitet, habe ich zur besseren Portabilität den folgenden Prolog geschrieben. Das Skript bootet sich selbst, selbst wenn #!/bin/sh ist eine nicht POSIX-konforme alte Bourne Shell. (Ich baue jede Version auf einer Solaris 10 VM).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"[email protected]"}
fi

# rest of the script here, executing in upgraded shell

Die Idee dabei ist, dass wir eine bessere Shell finden als die, unter der wir laufen, und das Skript mit dieser Shell erneut ausführen. Die txr_shell Umgebungsvariable gesetzt ist, damit das erneut ausgeführte Skript weiß, dass es sich um die erneut ausgeführte rekursive Instanz handelt.

(In meinem Skript ist die txr_shell Variable wird auch später für genau zwei Zwecke verwendet:Erstens wird sie als Teil einer informativen Meldung in der Ausgabe des Skripts ausgegeben. Zweitens wird es als SHELL installiert Variable im Makefile , sodass make wird diese Shell auch zum Ausführen von Rezepten verwenden.)

Auf einem System, auf dem /bin/sh Bindestrich ist, können Sie sehen, dass die obige Logik /bin/bash findet und führe das Skript damit erneut aus.

Auf einer Solaris 10-Box der /usr/xpg4/bin/sh wird aktiviert, wenn keine Bash gefunden wird.

Der Prolog ist in einem konservativen Shell-Dialekt unter Verwendung von test geschrieben für Dateiexistenztests und den ${@+"[email protected]"} Trick zum Erweitern von Argumenten für einige kaputte alte Shells (was einfach "[email protected]" wäre wenn wir uns in einer POSIX-konformen Shell befänden).

Lösung 3:

Alle Variationen der Bourne-Shell-Sprache sind im Vergleich zu modernen Skriptsprachen wie Perl, Python, Ruby, node.js und sogar (wohl) Tcl objektiv schrecklich. Wenn Sie etwas auch nur ein bisschen Kompliziertes tun müssen, werden Sie auf lange Sicht glücklicher sein, wenn Sie eines der oben genannten anstelle eines Shell-Skripts verwenden.

Der einzige Vorteil, den die Shell-Sprache gegenüber diesen neueren Sprachen immer noch hat, ist dieses Etwas nennt sich selbst /bin/sh garantiert auf allem existiert, was vorgibt, Unix zu sein. Dieses Etwas ist jedoch möglicherweise nicht einmal POSIX-kompatibel; Viele der älteren proprietären Unixe haben die von /bin/sh implementierte Sprache eingefroren und die Dienstprogramme im Standard PATH prior zu den von Unix95 geforderten Änderungen (ja, Unix95, vor zwanzig Jahren und Zählen). Es könnte eine Reihe von Unix95- oder, wenn Sie Glück haben, sogar POSIX.1-2001-Tools in einem Verzeichnis nicht geben auf dem Standard PATH (z.B. /usr/xpg4/bin ), aber es ist nicht garantiert, dass sie existieren.

Die Grundlagen von Perl sind jedoch wahrscheinlicher auf einer willkürlich ausgewählten Unix-Installation vorhanden sein als Bash. (Mit "Grundlagen von Perl" meine ich /usr/bin/perl existiert und ist einige , möglicherweise ziemlich alte Version von Perl 5, und wenn Sie Glück haben, sind auch die Module verfügbar, die mit dieser Version des Interpreters geliefert wurden.)

Deshalb:

Wenn Sie etwas schreiben, das überall funktionieren muss das vorgibt, Unix zu sein (z. B. ein "configure"-Skript), müssen Sie #! /bin/sh verwenden , und Sie müssen keinerlei Erweiterungen verwenden. Heutzutage würde ich unter diesen Umständen eine POSIX.1-2001-konforme Shell schreiben, aber ich wäre bereit, POSIXismen auszubessern, wenn jemand um Unterstützung für rostiges Eisen bitten würde.

Aber wenn Sie nicht sind etwas zu schreiben, das überall funktionieren muss, dann sollten Sie in dem Moment, in dem Sie versucht sind, überhaupt Bashismus zu verwenden, aufhören und das Ganze stattdessen in einer besseren Skriptsprache neu schreiben. Ihr zukünftiges Ich wird es Ihnen danken.

(Also wann ist ist es angebracht, Bash-Erweiterungen zu verwenden? Bei Erstbestellung:nie. In zweiter Ordnung:nur um die interaktive Bash-Umgebung zu erweitern - z. um eine intelligente Tab-Vervollständigung und ausgefallene Eingabeaufforderungen bereitzustellen.)


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

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

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

  4. Berechtigung für Composer in /usr/local/bin/ verweigert

  5. Wann muss ich #!/bin/bash und wann #!/bin/sh verwenden?

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

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

cmake --version zeigt auf /usr/bin/cmake, während cmake auf /usr/local/bin zeigt

Wann sollte ich /dev/shm/ verwenden und wann sollte ich /tmp/?

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

Chroot schlägt fehl - Befehl `/bin/bash' kann nicht ausgeführt werden:Keine solche Datei oder Verzeichnis