Wann muss ich #! / Bin / bash und wann #! / Bin / sh verwenden?

104

Wann ist #!/bin/bashbesser geeignet als #!/bin/shin einem Shell-Skript?

Hendré
quelle
55
Wenn Sie bashFunktionen und Syntax anstelle von shFunktionen und Syntax verwenden.
Mokubai
3
Wenn es jemand hilft, habe ich bemerkt , dass vimHighlight wird bashIsmen , wenn Ihr Skript das hat #!/bin/shshebang. Ich ändere es nur, bashwenn die Dinge haarig genug werden, um Bash-Features zu benötigen.
SeldomNeedy
@SeldomNeedy Einige der hervorgehobenen Funktionen funktionieren jedoch standardmäßig in jeder POSIX-Shell. $(...)ist besonders widerlich. Einige von ihnen sind auch subtil [ <(...)und cmd >& fileerhalten keine Fehlerhervorhebung, zum Beispiel haben sie keine spezielle Hervorhebung für das, was sie bedeuten, mit oder ohne g:is_bash]
Random832
@ Random832 Es sieht so aus, als ob in letzter Zeit in Vims Issue-Tracker einige Aktivitäten zu diesem Thema stattgefunden haben .
SeldomNeedy

Antworten:

150

Zusamenfassend:

  • Es gibt mehrere Shells, die eine Obermenge der POSIX-sh-Spezifikation implementieren . Auf verschiedenen Systemen kann /bin/shes sich um eine Verknüpfung zu ash, bash, dash, ksh, zsh usw. handeln. (Es wird jedoch immer sh-kompatibel sein - niemals csh oder fish.)

  • Solange Sie sich nur an shFeatures halten, können Sie (und sollten es wahrscheinlich auch) verwenden, #!/bin/shund das Skript sollte einwandfrei funktionieren, unabhängig davon, um welche Shell es sich handelt.

  • Wenn Sie anfangen, bash-spezifische Funktionen (z. B. Arrays) zu verwenden, sollten Sie bash speziell anfordern. Selbst wenn /bin/shbash bereits auf Ihrem System aufgerufen wird, wird es möglicherweise nicht auf allen anderen Systemen ausgeführt, und Ihr Skript wird dort nicht ausgeführt. (Dasselbe gilt natürlich auch für zsh und ksh.) Sie können Shellcheck verwenden , um Bashismen zu identifizieren.

  • Auch wenn das Skript nur für den persönlichen Gebrauch gedacht ist, werden Sie bemerken, dass sich einige Betriebssysteme /bin/shwährend eines Upgrades ändern - z. B. unter Debian war es Bash, wurde aber später durch einen sehr minimalen Gedankenstrich ersetzt. Skripte, die Bashismen verwendeten, aber #!/bin/shplötzlich kaputt waren.

Jedoch:

  • Auch #!/bin/bashist nicht sehr richtig. Auf verschiedenen Systemen kann bash in /usr/binoder /usr/pkg/binoder leben /usr/local/bin.

  • Eine zuverlässigere Option ist #!/usr/bin/env bash$ PATH. (Auch wenn das envTool selbst nicht streng garantiert ist, /usr/bin/envfunktioniert es immer noch auf mehr Systemen als dies der /bin/bashFall ist.)

Grawity
quelle
10
Gute Antwort. Meinten Sie, es CW zu machen?
Mokubai
3
Nur eine Bemerkung, wenn Bash ausgeführt wird /bin/sh, wird versucht, shFeatures zu imitieren (siehe Manpage ). Nicht sicher, ob Bash-spezifische Features in diesem Modus verfügbar sind. envtool ist ein posix tool, das es in den meisten Distributionen gibt, aber ich bin mir nicht sicher, da manche posix nicht respektieren.
Brice
8
Nein, tatsächlich gibt POSIX ausdrücklich an, dass nicht angenommen werden kann, dass es sich um Folgendes handelt /bin: " Anwendungen sollten beachten, dass der Standard-PATH für die Shell weder / bin / sh noch / usr / bin / sh sein kann und bestimmt werden sollte durch Abfrage des von getconf PATH zurückgegebenen PATH, wobei sichergestellt wird, dass der zurückgegebene Pfadname ein absoluter Pfadname und keine eingebaute Shell ist ". Siehe auch diese Frage und speziell diese Antwort .
Terdon
12
Hmm, na ja, das heißt, POSIX definiert eigentlich keinen portablen Weg, um #!Skripte arbeiten zu lassen?
Grawity
4
POSIX sh ist nicht Bourne: Es ist eher eine Ableitung des frühen ksh als ein direkter Nachkomme von Bourne. Sie ist einfach zu unterscheiden: echo foo ^ catemittiert foo ^ catin POSIX sh, und emittiert nur fooin Bourne (wie ^ein Rohr Charakter besteht).
Charles Duffy
18

Verwenden Sie den Shebang, der der Shell entspricht, die Sie tatsächlich zum Entwickeln und Debuggen Ihres Skripts verwendet haben. Dh wenn Ihre Login-Shell ist bashund Sie Ihr Skript als ausführbar in Ihrem Terminal ausführen, verwenden Sie #!/bin/bash. Gehen Sie nicht einfach davon aus, dass Sie, da Sie keine Arrays (oder bashFunktionen, die Ihnen bekannt sind) verwendet haben, die von Ihnen gewünschte Shell auswählen können. Es gibt viele subtile Unterschiede zwischen Shells ( echo, Funktionen, Schleifen, wie Sie es nennen), die ohne geeignete Tests nicht entdeckt werden können.

Bedenken Sie Folgendes: Wenn Sie das Programm verlassen #!/bin/bashund Ihre Benutzer es nicht haben, wird ihnen eine eindeutige Fehlermeldung angezeigt, wie z

Error: /bin/bash not found

Die meisten Benutzer können dieses Problem in weniger als einer Minute beheben, indem sie das entsprechende Paket installieren. Wenn Sie andererseits den shebang durch #!/bin/sheinen anderen ersetzen und auf einem System testen, zu dem /bin/shein Symlink besteht, /bin/bashsind Ihre Benutzer, die keinen haben bash, in Schwierigkeiten. Sie sehen höchstwahrscheinlich eine kryptische Fehlermeldung wie:

Error in script.sh line 123: error parsing token xyz

Dies kann Stunden dauern, und es wird keine Ahnung geben, welche Shell sie hätten verwenden sollen.

Es gibt nicht viele Gründe, warum Sie im shebang eine andere Schale verwenden möchten. Ein Grund ist, wenn die von Ihnen verwendete Shell nicht weit verbreitet ist. Eine andere shMöglichkeit besteht darin, eine Leistung zu erzielen, die auf einigen Systemen erheblich schneller ist, UND Ihr Skript wird zu einem Leistungsengpass. Testen Sie in diesem Fall Ihr Skript gründlich mit der Zielshell und ändern Sie dann den Shebang.

Dmitry Grigoryev
quelle
@Hastur Ich verstehe deinen Kommentar nicht. Der Shebang wird sicherlich definieren, welche Shell zum Ausführen des Skripts verwendet wird. Glauben Sie, dass meine Antwort etwas anderes impliziert?
Dmitry Grigoryev
1
Vergiss es. Ich habe den Teil "Warum Sie verwenden möchten" falsch verstanden ...
Hastur
6

Sie sollten immer nur verwenden #! /bin/sh.

Sie sollten niemals bash (oder zsh oder fish oder ...) -Erweiterungen in einem Shell-Skript verwenden.

Sie sollten immer nur Shell-Skripte schreiben, die mit einer Implementierung der Shell-Sprache funktionieren (einschließlich aller "Utility" -Programme, die mit der Shell selbst verbunden sind). In diesen Tagen können Sie wahrscheinlich nehmen POSIX.1-2001 ( nicht -2008) als maßgeblich für das, was die Shell und Dienstprogramme sind in der Lage, aber bewusst sein , dass Sie eines Tages aufgefordert werden , in den Hafen Ihr Skript an ein Legacy - System (zB Solaris oder AIX), deren Shell und Dienstprogramme um 1992 eingefroren wurden.

Was im Ernst ?!

Ja, im Ernst.

Hier ist die Sache: Shell ist eine schreckliche Programmiersprache. Das Einzige, was es zu bieten hat, /bin/shist der einzige Skript-Interpreter, über den jede Unix-Installation garantiert verfügt.

Hier ist die andere Sache: Einige Iterationen des Perl 5-Interpreters ( /usr/bin/perl) sind in einer zufällig ausgewählten Unix-Installation mit größerer Wahrscheinlichkeit verfügbar als in einer anderen (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bash. Andere gute Skriptsprachen (Python, Ruby, node.js usw. - ich werde sogar PHP und Tcl in diese Kategorie aufnehmen, wenn ich sie mit Shell vergleiche) sind in etwa so verfügbar wie Bash und andere erweiterte Shells.

Wenn Sie die Möglichkeit haben, ein Bash-Skript zu schreiben, können Sie stattdessen eine Programmiersprache verwenden, die nicht schrecklich ist.

Nun, einfache Shell-Skripte, die nur ein paar Programme in einer Sequenz von einem Cron-Job oder so ausführen, es ist nichts Falsches daran, sie als Shell-Skripte zu belassen. Einfache Shell-Skripte benötigen jedoch weder Arrays noch Funktionen oder [[Ähnliches. Und Sie sollten nur dann komplizierte Shell-Skripte schreiben, wenn Sie keine andere Wahl haben. Beispielsweise sind Autoconf-Skripte immer noch Shell-Skripte. Diese Skripte müssen jedoch auf jeder Inkarnation ausgeführt werden /bin/sh, die für das zu konfigurierende Programm relevant ist. und das heißt, sie können keine Erweiterungen verwenden. Sie müssen sich heutzutage wahrscheinlich nicht mehr um alte proprietäre Unixe kümmern, aber Sie sollten sich wahrscheinlich um aktuelle Open-Source-BSDs kümmern, von denen einige nicht installiert werdenbashstandardmäßig und eingebettete Umgebungen, die Ihnen nur eine minimale Shell und busybox.

Abschließend Sie der Moment finden sie wollen eine Funktion , die in der tragbaren Shell - Sprache nicht verfügbar ist, das ist ein Zeichen dafür , dass das Skript zu kompliziert worden ist ein Shell - Skript zu bleiben. Schreiben Sie es stattdessen in einer besseren Sprache um.

zwol
quelle
4
Entschuldigung, aber das ist ein bisschen albern. Ja, wenn Sie etwas schreiben, das i) verteilt und ii) an verschiedene Umgebungen verteilt wird, sollten Sie sich an basic halten sh. Dies ist jedoch weit davon entfernt, zu behaupten, dass Sie niemals andere Muscheln verwenden sollten. Täglich werden Tausende (wahrscheinlich noch viel mehr) Skripte geschrieben, und die überwiegende Mehrheit dieser Skripte wird immer nur auf einer einzelnen Maschine ausgeführt. Ihr Rat ist im Produktionscode sinnvoll, aber nicht für die täglichen "sysdaminy" -Jobs, für die die meisten Skripte geschrieben wurden. Wenn ich weiß, dass mein Skript immer nur von mir und auf einem Linux-Computer verwendet wird, ist bash in Ordnung.
Terdon
1
@terdon Der Punkt ist, dass Sie, wenn Sie die Möglichkeit haben, ein Bash-Skript zu schreiben, auch die Möglichkeit haben, ein Perl-Skript (oder was auch immer) zu schreiben, und dies ist immer die bessere Wahl.
zwol
@terdon Die Antwort ist sicher nicht dumm, dein Kommentar ist stattdessen einfach naiv. Shell-Skripte sind im Vergleich zu Perl und anderen Programmen fürchterlich zu debuggen. Für solche Skripte kann man problemlos vollständige IDEs mit grafischem Debugging und allem verwenden. Darüber hinaus werden die meisten Skripte, die jeden Tag für eine kleine Aufgabe geschrieben werden, in der Regel immer wieder geändert, wachsen, als Beispiele an Kollegen oder das Netz usw. kopiert. Es gibt höchstwahrscheinlich keinen Grund, ein solches Risiko einzugehen.
Thorsten Schöning
@ Thorstenschöning ich sagte ein wenig albern. Wenn dies Unix & Linux oder sogar Stack Overflow wäre, könnte ich sogar zustimmen. Wenn es sich um allgemeine Best Practices oder professionelle Programmierer handelt, ist es natürlich am besten, sich an POSIX sh zu halten. Dies ist jedoch Super User , eine Site, die sich an Endbenutzer richtet und den Leuten sagt, dass sie beim Schreiben von Shell-Skripten niemals bash oder zsh verwenden sollen, was meiner Meinung nach extrem ist.
Terdon
@terdon Es ist eigentlich mehr wichtig, meiner Meinung nach , Endanwendern vom Schreiben komplexe Shell - Skripten zu entmutigen. Profis verfügen im Durchschnitt über ein höheres Qualifikationsniveau (daher sind sie mit höherer Wahrscheinlichkeit in der Lage, die Tücken komplexer Shell-Skripte zu bewältigen), sollten mit einiger Präzision wissen, in welche Umgebungen sie portierbar sein müssen, und dafür bezahlt werden, dass sie nicht aufgeben frustriert.
zwol
4

Im Allgemeinen wird die schnellere Shell verwendet, wenn Zeit wichtiger als Funktionalität ist. sh ist häufig zum Bindestrich geneigt und wird häufig für Cron-Tasks oder Batch-Vorgänge von root verwendet, bei denen jede (Nano-) Sekunde zählt.

mckenzm
quelle
2
Das Laden eines zusätzlichen Shell-Interpreters aus dem Dateisystem kann einen solchen Vorteil zunichte machen, und alles, wo Nanosekunden zählen (die in der Größenordnung eines tatsächlichen Maschinenzyklus liegen), ist die Domäne von C- und Assembler-Programmierern.
Rackandboneman
Nanosekunden machen bei Cron-Jobs nur dann einen Unterschied, wenn Sie Milliarden davon haben und Cron sowieso nicht in der Lage ist, mit so vielen Jobs umzugehen.
Dmitry Grigoryev
1
Auch wenn McKenzm ein bisschen übertrieben hat, es ist nicht zu leugnen, dass es besser ist, mehr Leistung zu haben! Lass dich nicht auf den "Nano" Teil ein. (Nanosekunden zählen nicht einmal in C, es sei denn, es ist eine innere Schleife in einem Echtzeitsystem oder so!)
Jpaugh
1
@jpaugh Sofern Sie nicht mit einem (Legacy-) System arbeiten, das davon ausgeht, dass bestimmte Vorgänge mindestens einen bestimmten Zeitraum in Anspruch nehmen. Es passiert!
JAB
3

Um es noch kurz zu machen: Verwenden Sie shdiese Option , wenn die Portierbarkeit auf den meisten Systemen am wichtigsten ist und bashwenn Sie einige ihrer spezifischen Funktionen, z. B. Arrays, verwenden möchten, sofern die Version von bash dies unterstützt.

Spencer Williams
quelle
1
  • sh (für die meisten Fälle), bash, falls speziell erforderlich (oder ksh oder was auch immer)

Der sicherste Weg. Wenn hart codiert, wäre das / usr / bin /, shellOfChoiceaber eine neue Konvention, die ich immer jetzt zu verwenden versuche - da sich die 'Standardpositionen' über eine Änderung ändern können, ist PATH:

#! / usr / bin / env sh oder
#! / usr / bin / env bash
oder für zB Perl-Skripte
#! / usr / bin / env perl -w

Wenn Sie einen Grund dafür haben, dass ein Skript NIEMALS automatisch einen neuen PATH aufnimmt, lassen Sie es natürlich weiterhin fest codieren - und dann sollte / usr / bin / something der wahrscheinlichste Pfad sein.

Michael Felt
quelle