it-swarm.com.de

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

Normalerweise arbeite ich mit Ubuntu LTS-Servern, die meines Wissens nach Symlink /bin/sh Zu /bin/dash. Viele andere Distributionen verknüpfen /bin/sh Mit /bin/bash.

Daraus geht hervor, dass ein Skript, das oben #!/bin/sh Verwendet, möglicherweise nicht auf allen Servern auf die gleiche Weise ausgeführt wird.

Gibt es eine empfohlene Vorgehensweise, welche Shell für Skripte verwendet werden soll, wenn eine maximale Portabilität dieser Skripte zwischen Servern gewünscht wird?

37
cherouvim

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

  1. Am tragbarsten: Verwenden Sie ein #!/bin/sh Shebang und verwende nur die grundlegende Shell-Syntax, die im POSIX-Standard angegeben ist. Dies sollte auf so ziemlich jedem POSIX/Unix/Linux-System funktionieren. (Nun, mit Ausnahme von Solaris 10 und früheren Versionen, die das echte Vermächtnis von Bourne Shell hatten und vor POSIX so nicht konform waren wie /bin/sh.)

  2. Am zweithäufigsten tragbar: Verwenden Sie ein #!/bin/bash (oder #!/usr/bin/env bash) Shebang-Linie und bleib bei Bash v3-Funktionen. Dies funktioniert auf jedem System mit Bash (am erwarteten Speicherort).

  3. Am dritthäufigsten tragbar: Verwenden Sie ein #!/bin/bash (oder #!/usr/bin/env bash) Shebang-Linie und benutze 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 tragbar: Verwenden Sie ein #!/bin/sh Shebang und verwende 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 (wie z. B. aktuelle Ubuntu-Versionen). Tu das 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 bietet, die Sie für das Skript benötigen. Verwenden Sie für maximale Portabilität Option 1, aber meiner Erfahrung nach sind einige Bash-Funktionen (wie Arrays) hilfreich genug, um mit Nummer 2 fortzufahren.

Das Schlimmste, was Sie tun können, ist # 4 mit dem falschen Shebang. Wenn Sie sich nicht sicher sind, welche Funktionen POSIX und welche Bash-Erweiterungen sind, bleiben Sie entweder bei einem Bash-Shebang (d. H. Option 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 den Verlauf und die Unterschiede zwischen Shells in der Unix- und Linux-Frage "Was bedeutet es, sh-kompatibel zu sein?" und der Stackoverflow-Frage "Unterschied zwischen sh und bash" " .

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

Einer der schlimmsten Befehle in Bezug auf Portabilität ist einer der grundlegendsten: echo. Jedes Mal, wenn Sie es mit Optionen verwenden (z. B. echo -n oder echo -e) oder mit Escapezeichen (Backslashes) in der zu druckenden Zeichenfolge führen verschiedene Versionen unterschiedliche Aktionen aus. Wenn Sie eine Zeichenfolge ohne Zeilenvorschub oder mit Escapezeichen in der Zeichenfolge drucken möchten, verwenden Sie stattdessen printf (und lernen Sie, wie es funktioniert - es ist komplizierter als echo). Der Befehl ps lautet auch ein Durcheinander .

Eine weitere allgemeine Sache, auf die Sie achten sollten, sind die jüngsten/GNUish-Erweiterungen der Befehlsoptionssyntax: Das alte (Standard-) Befehlsformat besteht darin, dass auf den Befehl Optionen folgen (mit einem einzigen Bindestrich, und jede Option ist ein einzelner Buchstabe), gefolgt von Befehlsargumente. Neuere (und häufig nicht tragbare) Varianten enthalten lange Optionen (normalerweise eingeführt mit --), wobei Optionen nach Argumenten stehen und --, um Optionen von Argumenten zu trennen.

66
Gordon Davisson

Im Skript ./configure, Das die TXR-Sprache für die Erstellung vorbereitet, habe ich den folgenden Prolog geschrieben, um die Portabilität zu verbessern. Das Skript wird bootstrap selbst, auch wenn #!/bin/sh Eine nicht POSIX-konforme alte Bourne-Shell ist (ich erstelle 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 hier ist, dass wir eine bessere Shell finden als die, unter der wir ausgeführt werden, und das Skript mit dieser Shell erneut ausführen. Die Umgebungsvariable txr_Shell Wird festgelegt, damit das erneut ausgeführte Skript weiß, dass es sich um die erneut ausgeführte rekursive Instanz handelt.

(In meinem Skript wird die Variable txr_Shell Nachfolgend auch für genau zwei Zwecke verwendet: Erstens wird sie als Teil einer informativen Nachricht in der Ausgabe des Skripts gedruckt. Zweitens wird sie als Shell Variable in Makefile, so dass make diese Shell auch zum Ausführen von Rezepten verwendet.)

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

Auf einer Solaris 10-Box wird /usr/xpg4/bin/sh Aktiviert, wenn kein Bash gefunden wird.

Der Prolog ist in einem konservativen Shell-Dialekt geschrieben, wobei test für Dateienxistenztests und der Trick ${@+"[email protected]"} Zum Erweitern von Argumenten für einige kaputte alte Shells verwendet werden (was einfach "[email protected]" wenn wir in einer POSIX-konformen Shell wären).

5
Kaz

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 tun müssen, das auch nur ein bisschen kompliziert ist, sind Sie auf lange Sicht glücklicher, wenn Sie eines der oben genannten anstelle eines Shell-Skripts verwenden.

Der einzige Vorteil, den die Shell-Sprache gegenüber diesen neueren Sprachen noch hat, ist, dass etwas, das sich selbst /bin/sh Nennt, garantiert für alles existiert, was vorgibt, Unix zu sein. Dies ist jedoch möglicherweise nicht einmal POSIX-kompatibel. Viele der älteren proprietären Unixe haben die von /bin/sh und den Dienstprogrammen im Standard-PATH prior implementierte Sprache an den von Unix95 geforderten Änderungen eingefroren (ja, Unix95, vor zwanzig Jahren und gezählt ). Möglicherweise gibt es eine Reihe von Unix95- oder sogar POSIX.1-2001-Tools in einem Verzeichnis nicht auf dem Standardpfad (z. B. /usr/xpg4/bin), Aber diese sind nicht garantiert zu existieren.

Die Grundlagen von Perl sind jedoch wahrscheinlicher, um in einer willkürlich ausgewählten Unix-Installation vorhanden zu sein als Bash. (Mit "den Grundlagen von Perl" meine ich, dass /usr/bin/Perl Existiert und einige, möglicherweise eine ziemlich alte Version von Perl 5, und wenn Sie Glück haben, die Menge der Module, die Die mit dieser Version des Dolmetschers gelieferte Version ist ebenfalls erhältlich.)

Deshalb:

Wenn Sie etwas schreiben, das funktionieren muss überall das angeblich Unix ist (z. B. ein "configure" -Skript), müssen Sie #! /bin/sh, und Sie müssen keinerlei Erweiterungen verwenden. Heutzutage würde ich unter diesen Umständen 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 etwas schreiben, das überall funktionieren muss, sollten Sie in dem Moment, in dem Sie versucht sind, überhaupt einen Bashismus zu verwenden, anhalten und das Ganze stattdessen in einer besseren Skriptsprache umschreiben. Dein zukünftiges Selbst wird es dir danken.

(Also wenn ist ist es angebracht, Bash-Erweiterungen zu verwenden? Zur ersten Bestellung: niemals. Zur zweiten Ordnung: nur um die interaktive Bash-Umgebung zu erweitern - z. um intelligente Tab-Vervollständigung und ausgefallene Eingabeaufforderungen bereitzustellen.)

2
zwol