Ist es richtig, / bin / sh im Hashbang zu verwenden, wenn die Bourne-Shell in einer Distribution nicht verfügbar ist?

15

Im Allgemeinen enthalten Shell - Skripten den folgenden Kommentar in der ersten Zeile der Skriptdatei: #!/bin/sh. Nach meinen Recherchen wird dies als "Hash Bang" bezeichnet und ist ein herkömmlicher Kommentar. Dieser Kommentar informiert Unix darüber, dass diese Datei von der Bourne-Shell unter dem Verzeichnis ausgeführt wird /bin.

Meine Frage beginnt in diesem Punkt. Bisher habe ich diesen Kommentar nicht gerne gesehen #!/bin/bash. Es ist immer so #!/bin/sh. Ubuntu-Distributionen verfügen jedoch nicht über das Bourne Shell-Programm. Sie haben die Bourne Again Shell (Bash).

Ist es in diesem Punkt richtig, den Kommentar #!/bin/shin Shell-Skripten zu platzieren, die in Ubuntu-Distributionen geschrieben wurden?

Goktug
quelle
1
Siehe meine Antwort auf diese Serverfehlerfrage: #! / Bin / sh vs #! / Bin / bash für maximale Portabilität .
Gordon Davisson
Häufiger genanntshebang
fpmurphy

Antworten:

16

#!/bin/shsollte auf allen Unix- und Unix-ähnlichen Distributionen funktionieren. Es wird allgemein als das portabelste Hashbang angesehen, solange Ihr Skript POSIX-kompatibel ist. Die /bin/shShell soll eine Shell sein, die den POSIX-Shell-Standard implementiert, unabhängig davon, was die eigentliche Shell ist, die sich als /bin/shShell ausgibt.


#!/bin/shist normalerweise nur ein Link, da die Bourne-Shell nicht mehr gepflegt wird. Auf vielen Unix-Systemen /bin/shwird es eine Verknüpfung zu /bin/kshoder /bin/ashauf vielen RHEL-basierten Linux-Systemen wird es eine Verknüpfung zu sein /bin/bash, auf Ubuntu und vielen Debian-basierten Systemen ist es jedoch eine Verknüpfung zu /bin/dash. Wenn alle Shells als aufgerufen werden sh, wird der POSIX-Kompatibilitätsmodus aktiviert.

Der Hashbang ist jedoch ein wichtiger Platzhalter, da er eine viel größere Portabilität als andere Methoden ermöglicht, sofern Ihr Skript ausschließlich POSIX-kompatibel ist (wiederholt, um die Wichtigkeit zu betonen).

Hinweis: Beim bashAufrufen im POSIX-Modus sind weiterhin einige Nicht-POSIX-Elemente wie [[Arrays und mehr zulässig. Diese Dinge können auf einem Nicht- bashSystem fehlschlagen .

Jesse_b
quelle
3
"Unter Ubuntu ist es eine Verknüpfung zu / bin / dash" und allgemein zu anderen Debian-basierten Systemen. IIRC unter FreeBSD / GhostBSD ist auch ein Symlink zu /bin/ash.
Sergiy Kolodyazhnyy
Um vollständig portabel zu sein, fügen Sie ein Leerzeichen zwischen dem shebang und dem (absoluten) Interpreterpfad ein. Einige Systeme suchen #! /statt nur #!.
Simon Richter
5
@SimonRichter Ein Hinweis hierzu wäre schön zu sehen.
Kusalananda
@SergiyKolodyazhnyy Die auf FreeBSD installierte Version von sh ist ash und kein Symlink.
Rob
2
@Kusalananda, hmm, diese Seite sagt, dass dies ein Mythos ist, so dass es wahrscheinlich ignoriert werden kann.
Simon Richter
12

Du hast es verkehrt herum. /bin/shist heutzutage fast nie mehr eine Bourne-Muschel, und es ist dann, wenn Sie ein Problem haben, wenn Sie einen She #! /bin/sh-Bang verwenden.

Die Bourne-Shell war eine Shell, die Ende der 70er Jahre geschrieben wurde und die vorherige Thompson-Shell (auch als "Shell" bezeichnet sh) ersetzte . In den frühen 80er Jahren hat David Korn einige Erweiterungen für die Bourne-Shell geschrieben, einige Fehler behoben und Ungeschicklichkeiten im Design behoben (und einige eingeführt) und sie als Korn-Shell bezeichnet.

In den frühen 90er Jahren spezifizierte POSIX die sh Sprache basierend auf einer Teilmenge der Korn-Shell, und die meisten Systeme haben ihre Sprache jetzt /bin/shentweder in die Korn-Shell oder in eine Shell geändert , die dieser Spezifikation entspricht. Bei BSDs änderten sie schrittweise /bin/shdie Almquist-Shell (nachdem sie die Bourne-Shell aus Lizenzgründen nicht mehr verwenden konnten), einen Klon der Bourne-Shell mit einigen ksh-Erweiterungen, sodass sie POSIX-kompatibel wurde.

Heutzutage haben alle POSIX-Systeme eine Shell, die POSIX-kompatibel ist sh(meistens, aber nicht unbedingt /bin, gibt POSIX nicht den Pfad der angegebenen Dienstprogramme an). Es basiert im Allgemeinen entweder auf ksh88, ksh93, pdksh, bash, ash oder zsh², jedoch nicht auf der Bourne-Shell, da die Bourne-Shell niemals POSIX-kompatibel war³. Ein paar dieser Schalen ( bash, zsh, yashund einige pdkshDerivate ermöglichen eine POSIX-kompatiblen Modus , wenn aufgerufen , wie shund sind weniger konform auf andere Weise).

bash(die GNU-Antwort auf die Korn-Shell) ist tatsächlich die einzige Open-Source-Shell (und man könnte sagen, dass sie derzeit nur gewartet wird, da die anderen in der Regel auf ksh88 basieren, das seit den 90er Jahren keine neuen Funktionen mehr hat), die als zertifiziert wurde POSIX-konform sein sh(im Rahmen der macOS-Zertifizierung).

Wenn Sie ein Skript mit einem #! /bin/sh -She-Bang schreiben , sollten Sie eine Standardsyntax verwenden sh(und auch Standardsyntax für die in diesem Skript verwendeten Dienstprogramme verwenden, wenn Sie portabel sein möchten. Es ist nicht nur die Shell, die bei der Interpretation einer Shell beteiligt ist Skript), dann spielt es keine Rolle, welche Implementierung dieses Standard- shSyntax-Interpreters verwendet wird ( ksh, bash...).

Es spielt keine Rolle, dass diese Shells Erweiterungen über dem Standard haben, solange Sie sie nicht verwenden. Es ist wie beim Schreiben von C-Code. Solange Sie Standard-C-Code schreiben und keine Erweiterungen des einen gccoder anderen Compilers verwenden , sollte der Code unabhängig von der Compilerimplementierung einwandfrei kompiliert werden, sofern der Compiler kompatibel ist.

Hier mit dem #! /bin/shsie Bang, Ihr Hauptproblem wäre die Systeme, in denen /bin/shist die Bourne - Shell , dass zum Beispiel nicht unterstützt Standard - Features wie $((1+1)), $(cmd), ${var#pattern}... , in dem Fall , dass Sie Umgehungen benötigen wie:

#! /bin/sh -
if false ^ true; then
  # in the Bourne shell, ^ is an alias for |, so false ^ true returns
  # true in the Bourne shell and the Bourne shell only.
  # Assume the system is Solaris 10 (older versions are no longer maintained)
  # You would need to adapt if you need to support some other system
  # where /bin/sh is the Bourne shell.
  # We also need to fix $PATH as the other utilities in /bin are
  # also ancient and not POSIX compatible.
  PATH=`/usr/xpg6/bin/getconf PATH`${PATH:+:}$PATH || exit
  export PATH
  exec /usr/xpg4/bin/sh "$0" "$@"
  # that should give us an environment that is mostly  compliant
  # to the Single UNIX Specification version 3 (POSIX.2004), the
  # latest supported by Solaris 10.
fi
# rest of the script

Übrigens ist Ubuntu /bin/shnicht bashstandardmäßig. Es ist dashheutzutage eine Shell, die auf NetBSD basiert sh, selbst auf der Almquist-Shell, die größtenteils POSIX-kompatibel ist, außer dass sie keine Multi-Byte-Zeichen unterstützt. Auf Ubuntu und andere Debian-basierten Systemen können Sie wählen zwischen bashund dashfür /bin/shmit dpkg-reconfigure dash) 4 . shMit Debian gelieferte Skripte sollten in beiden Shells genauso funktionieren, wie sie nach dem Debian-Richtlinienstandard (einer Obermenge des POSIX-Standards) geschrieben wurden. Sie werden wahrscheinlich feststellen, dass sie auch in zshder shEmulation funktionieren oder bosh(wahrscheinlich nicht ksh93oder yashnicht mit localeingebautem Code (erforderlich für die Debian-Richtlinie, aber nicht für POSIX)).

Alle Systeme im Geltungsbereich von unix.stackexchange.com haben sh irgendwo eine POSIX . Die meisten von ihnen haben eine /bin/sh(Sie finden vielleicht sehr seltene, die kein /binVerzeichnis haben, aber das interessiert Sie wahrscheinlich nicht), und das ist im Allgemeinen ein POSIX- shInterpreter (und in seltenen Fällen eine (nicht standardmäßige) Bourne-Shell) ).

Aber shist der einzige ausführbare Shell-Interpreter, den Sie auf einem System finden können? Für die anderen Shells können Sie sicher sein, dass macOS, Cygwin und die meisten GNU / Linux-Distributionen installiert sind bash. Von SYSV abgeleitete Betriebssysteme (Solaris, AIX ...) haben im Allgemeinen ksh88, möglicherweise ksh93. OpenBSD, MirOS wird ein pdksh-Derivat haben. MacOS wird haben zsh. Aber davon gibt es keine Garantie. Es kann weder garantiert werden, ob bashnoch eine dieser anderen Shells in /binoder an einem anderen Ort installiert wird (dies ist beispielsweise bei /usr/local/binBSD-Installationen normalerweise der Fall). Und natürlich keine Garantie für die Version der Shell, die installiert wird.

Beachten Sie, dass dies #! /path/to/executablekeine Konvention ist , sondern eine Funktion aller Unix-ähnlichen Kernel ( eingeführt in den frühen 80ern von Dennis Ritchie ), mit der beliebige Dateien ausgeführt werden können, indem der Pfad des Interpreters in einer ersten Zeile beginnend mit angegeben wird #!. Es kann sich um eine beliebige ausführbare Datei handeln.

Wenn Sie eine Datei mit der ersten Zeile beginnt , deren ausführen #! /some/file some-optional-arg, beendet der Kernel - Ausführung nach oben /some/filemit some-optional-arg, dem Pfad des Skripts und die ursprünglichen Argumente als Argumente. Sie können diese erste Zeile machen, um #! /bin/echo testzu sehen, was passiert:

$ ./myscript foo
test ./myscript foo

Wenn Sie /bin/sh -anstelle von verwenden /bin/echo test, wird der Kernel ausgeführt /bin/sh - ./myscript foo, shder in gespeicherte Inhaltscode interpretiert myscriptund die erste Zeile als Kommentar ignoriert (beginnt mit #).


¹ Wahrscheinlich /bin/shist Solaris 10 das einzige System, auf dem heutzutage jeder von uns auf ein System stößt, das auf der Bourne-Shell basiert. Solaris ist eines der wenigen Unices, die sich aus Gründen der Abwärtskompatibilität dafür entschieden haben, eine Bourne-Shell dort zu belassen (die POSIX- shSprache ist nicht vollständig) Abwärtskompatibel mit der Bourne-Shell) und (zumindest für Desktop- und vollständige Server-Bereitstellungen) haben POSIX an shanderer Stelle (in /usr/xpg4/bin/sh, basierend auf ksh88), aber das hat sich in Solaris 11 geändert, wo /bin/shjetzt ksh93 ist. Die anderen sind meistens verstorben.

² Das /bin/shvon MacOS / X war früher,zsh wurde aber später auf geändert bash. Es ist nicht zshdas Hauptaugenmerk, als POSIX- shImplementierung verwendet zu werden. Der shModus besteht hauptsächlich darin, sourcePOSIX- shCode in zshSkripte einbetten oder aufrufen zu können

³ Vor kurzem hat @schily die OpenSolaris-Shell (basierend auf der SVR4-Shell, basierend auf der Bourne-Shell) so erweitert, dass sie POSIX-kompatibel ist. boshMir ist jedoch noch nicht bekannt, dass sie auf einem beliebigen System verwendet wird. Zusammen ksh88damit ist es eine zweite POSIX-kompatible Shell, die auf dem Code der Bourne-Shell basiert

4 In älteren Versionen können Sie auch mkshPOSIX verwenden lksh. Das ist die MirOS-Shell (ehemals MirBSD), die auf pdksh basiert, selbst basiert auf der Forsyth-Shell (eine weitere Neuimplementierung der Bourne-Shell).

Stéphane Chazelas
quelle
Kleinigkeit (über den Shell-Code in der Mitte der Antwort): Solltest du den nicht gleich exec sh "$0" "$@"nach dem Einstellen verwenden PATH? Das sollte shan der richtigen Stelle abgeholt werden, hätte ich gedacht.
Kusalananda
1
@Kusalananda, das sollte funktionieren, ist aber riskanter, da wir in einer Endlosschleife enden könnten, wenn etwas nicht stimmt (wie sh mit falschen Berechtigungen, getconf modified ...). Wie auch immer, der Rest ist spezifisch für Solaris 10, so dass wir auch alles fest programmieren können, einschließlich des Inhalts von $PATH.
Stéphane Chazelas
Alle Zitate für OSX, die ksh als POSIX-Shell verwenden. Die Standard-Shell war früher tcsh, aber ich erinnere mich nicht, dass bash nicht verwendet wurde, zumal die Korn-Shell erst nach dem Erscheinen von OSX Open Source war
user151019
1
@Mark, ich habe nicht gesagt, dass OSX jemals ksh verwendet hat. Ich habe gesagt, dass es früher zsh war und sie zu bash gewechselt sind (ein spezieller Build von bash).
Stéphane Chazelas
1
@fpmurphy, wenn man sich die frühen Versionen ansieht, war bash bereits auf der Seite von ksh, wo immer ksh nicht mit der Bourne-Shell kompatibel war. Während Sie bis bash2 warten mussten, um auf das gleiche Funktionsniveau wie ksh88 zu gelangen, verfügte bash anfangs bereits über verschiedene Erweiterungen von ksh, wie $(...)z , ksh-style function syntax ...
Stéphane Chazelas
8

Ja, Sie können es #!/bin/shin einem Skript verwenden, da /bin/shes (hoffentlich) auf solchen Systemen vorgesehen ist, normalerweise über einen Link, der das bashVerhalten (mehr oder weniger) wie ein normaler Link verhält sh. Hier ist ein Centos7 System, zum Beispiel, dass Links shzu bash:

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 

Sie können auch verwenden, #!/bin/bashwenn Sie ein bashSkript nur für dieses System schreiben und Funktionen verwenden möchten bash. Solche Skripte werden jedoch unter Portabilitätsproblemen leiden, zum Beispiel unter OpenBSD, wo bashnur installiert wird, wenn der Administrator sich die Mühe macht, es zu installieren (ich nicht) und es dann installiert wird /usr/local/bin/bash, nicht /bin/bash. Ein ausschließlich POSIX- #!/bin/shSkript sollte portabler sein.

Thrig
quelle
Beachten Sie Folgendes: "Beim Aufrufen als shwechselt Bash nach dem Lesen der Startdateien in den POSIX-Modus." - gnu.org/software/bash/manual/bashref.html#Bash-POSIX-Mode
Glenn Jackman
2
Wenn ich in meinem Job Skripte für RHEL-Server schreibe, verwende ich #!/bin/bashgenau diese, um die Vorteile von Nicht-POSIX-Funktionen nutzen zu können.
Monty Harder
@MontyHarder IIRC auf RHEL /bin/shist ein Symlink zu bash, ist das richtig? Ich bin mir jedoch sicher, dass es CentOS ist.
Sergiy Kolodyazhnyy
1
@SergiyKolodyazhnyy Ja, auf dem RHEL-Server, den ich gerade überprüft habe, ist es ein Symlink zu bash. Die Binärdatei weiß, mit welchem ​​Namen sie aufgerufen wurde, und wenn sie aufgerufen wird, verhält sie sich shwie Bourne der alten Schule sh.
Monty Harder
6

Du hast gefragt

Ist es richtig, den Kommentar #! / bin / sh in Shell-Skripten zu platzieren, die in Ubuntu-Distributionen geschrieben wurden?

Die Antwort hängt davon ab, was Sie im Shell-Skript schreiben.

  • Wenn Sie ausschließlich portable POSIX-kompatible Skripte verwenden und keine bash-spezifischen Befehle verwenden, können Sie diese verwenden /bin/sh.

  • Wenn Sie wissen, dass Sie das Skript immer nur auf einem Computer mit bash verwenden und die bash-spezifische Syntax verwenden möchten, sollten Sie verwenden/bin/bash

  • Wenn Sie sicherstellen möchten, dass das Skript auf einer Reihe von Unix-Computern ausgeführt werden kann, sollten Sie nur POSIX-kompatible Syntax und verwenden/bin/sh

  • Wenn Sie regelmäßig verwenden , eine andere Shell (zB ksh , zsh oder tcsh ), und dass die Syntax in Ihrem Skript verwenden möchten, dann Sie sollten den entsprechenden Interpreter (wie /bin/ksh93, /bin/zsh, oder /bin/tcsh)

Stobor
quelle
3

Das "#!" Kommentar verwendet nicht immer /bin/bashoder /bin/sh. Es wird nur aufgeführt, was der Interpreter sein soll, nicht nur für Shell-Skripte. Zum Beispiel beginnen meine Python-Skripte normalerweise mit #!/usr/bin/env python.

Nun ist die Differenz zwischen #!/bin/shund #!/bin/bashist , dass /bin/shnicht immer ein symbolischer Link zu /bin/bash. Oft aber nicht immer. Ubuntu ist hier eine bemerkenswerte Ausnahme. Ich habe gesehen, dass Skripte unter CentOS einwandfrei funktionieren, unter Ubuntu jedoch nicht, weil der Autor die bash-spezifische Syntax mit verwendet hat #!/bin/sh.

Aragaer
quelle
1

Es tut mir leid, dass Sie all diese tollen Antworten mit kaltem Wasser übergossen haben, die besagen, dass /bin/shsie in allen Unix-Systemen vorhanden sind - es sei denn, es handelt sich um das am häufigsten verwendete Unix-System aller Zeiten: Android.

Android hat seine Shell /system/bin/shund es gibt normalerweise keine Möglichkeit, eine /bin/shVerknüpfung herzustellen , auch nicht auf einem verwurzelten System (aufgrund der Art und Weise, wie das System durch die Verwendung von Selinux und Capabilities (7) -Bounding-Sets gesperrt wird).

Für diejenigen, die sagen werden, dass Android nicht POSIX-konform ist, ebenso wenig wie die meisten Linux- und BSD-Distributionen. Und die Existenz von /bin/shmit diesem Weg ist nicht durch die Norm vorgeschrieben :

Anwendungen sollten beachten , dass der Standard PATHan die Schale kann auch nicht angenommen werden /bin/shoder /usr/bin/sh, und soll durch Abfrage der bestimmt werden PATHdurch zurückgegeben getconf PATH, um sicherzustellen , dass der zurückgegebene Pfad ein absoluter Pfadname ist und kein Shell eingebaut.

Mosvy
quelle
Während alle Systeme, die versuchen, POSIX-kompatibel zu sein, zahlreiche Konformitätsfehler aufweisen (einschließlich zertifizierter), beabsichtigen Android (und die meisten anderen eingebetteten Systeme wie Ihr Router oder Ihr Glühbirnen-Betriebssystem) nicht , POSIX-kompatibel zu sein. Android ist hier nicht zum Thema, außer wenn es um die POSIX-Oberfläche geht.
Stéphane Chazelas
@Christopher, welcher getconf? Wäre dies beispielsweise unter Solaris 11.4 der Fall /usr/bin? Der in /usr/xpg4/bin(für SUSv2-Konformität), der in /usr/xpg6/bin(für SUSv3-Konformität)? /usr/xpg7/bin(für SUSv4)?
Stéphane Chazelas
@ StéphaneChazelas Wenn wir die Absichten beurteilen wollen, kann ich leicht ein paar Zitate von Linus Torvalds oder Theo de Raadt herausfinden, dass ihnen POSIX egal ist ;-) Und die meisten eingebetteten Systeme basieren auf Linux + busybox, die kaum weniger POSIX-konform ist als das typische Unix-ähnliche System. Und a) Android ist nicht wirklich ein "eingebettetes" System und b) ein Android-System könnte POSIX-kompatibel gemacht werden, ohne eine Shell /bin/sh
einzubauen
1
@mosvy, was du sagst, ist größtenteils wahr. Aber Torvalds kann nur für den Linux-Kernel sprechen. Chet Ramey kümmert sich um die POSIX-Konformität für Bash, RedHat um die POSIX-Konformität (sie sponsern die Austin-Gruppe und nehmen an ihren Gruppentreffen teil, sie warten einen Großteil der GNU-Software). Die Idee ist, dass POSIX der einzige Standard ist, den die meisten Unix-ähnlichen Systeme betrachten. Die Benutzer legen Wert auf POSIX-Kompatibilität, da die Software leichter auf verschiedene Systeme portiert werden kann. Es ist nicht ideal, aber es ist besser als nichts. Android hat eine eigene Programmierschnittstelle, die POSIX ist da eigentlich kein Thema.
Stéphane Chazelas