Aus Linux-Magazin 12/2012

Shellskripte aus der Stümper-Liga – Folge 23: Dash statt Bash

© Fotograf, 123RF.com

Mit Debian Squeeze und Ubuntu 6.10 flog die Bash aus diesen Distributionen als Default-Shell raus. Die Begründung: nicht Posix-konform, zu langsam, zu hoher Speicherbedarf. Neue Perle am Muschelstrand ist die Dash, die Debian Almquist Shell.

Wie das ganze Unix-Universum hat auch die Shell eine komplizierte Genealogie. Da sich Software-Entwickler selten damit abfinden, dass ihre Software fertig ist, wächst der Funktionsumfang von Version zu Version. Die Bash ist dafür ein gutes Beispiel, so hat die recht neue Version 4 eine ganze Reihe von nützlichen Erweiterungen gebracht [1]. Mit jeder Erweiterung entfernt sich die Bash aber ein Stück weiter vom Posix-Standard. Als kleinster gemeinsamer Nenner stellt er eine minimale Interoperabilität zwischen verschiedenen Unix-Systemen sicher – allerdings eher auf dem Papier als in der Realität.

Ein weiterer Effekt bei Erweiterungen ist behäbiger Art: Die Bash wird größer, hängt von mehr Bibliotheken ab, lädt langsamer und parst länger an der ständig wachsenden Sprachsyntax. Im interaktiven Betrieb merkt der Anwender davon wenig. Aber beim Booten starten viele meist kurze Skripte, und hier addieren sich die Ladezeiten zu merkbaren Verzögerungen. Die Macher einiger Distributionen gingen daher auf Muschelsuche und wurden mit der Dash [2] fündig.

Die Default-Shell

Der Begriff “Default-Shell” ist etwas schwammig, es gibt hierfür keinen offiziellen Standard. Üblich auf Unix-Systemen ist ein Symlink von »/bin/sh« auf eine Shell-Implementation, die dann diese Rolle einnimmt. Die Macher von Debian und Ubuntu änderten diesen Link von der Bash auf die Dash ([3] für Unbuntu). Seither ziehen alle Skripte mit dem Shebang »#!/bin/sh”« automatisch die Dash an. Der Effekt: Von einem Augenblick auf den anderen weisen Skripte mit diesem Shebang, die trotzdem Bash-Konstrukte verwenden, Syntaxfehler auf.

Im Grunde trifft die Bash-Entwickler an dieser Verwirrung eine erhebliche Mitschuld. In der Manpage steht, dass die Bash – als »sh« aufgerufen – sich »sh« -kompatibel verhält. Darunter versteht die Bash aber nicht die Beschränkung auf jene Syntax, die die klassische Shell unterstützt, sondern nur das Verhalten bei speziellen Syntaxkonstrukten, die die Bash inzwischen anders umsetzt.

Der Wechsel zur Dash erzeugte einen gewissen Aufruhr, denn nicht nur alle möglichen Skripte hatten Probleme, sondern auch die vielen Makefiles, die Bash-Code verwenden. Die Aufregung legte sich etwas, als herauskam, dass die Debian- und Ubuntu-Maintainer die Bash nicht komplett entmachten, denn in beiden Systemen bleibt sie die Default-Login-Shell: Solange Admins neue Benutzer mit Tools wie »adduser« oder »useradd« anlegen, erhalten diese – dank eines entsprechenden »/etc/adduser.conf« -Eintrags – die Bash als Login-Shell.

Mager, mager

Jede Medaille hat bekanntlich zwei Seiten: Der Preis für eine schlanke Shell ist eine magere Syntax. Wer nur ein kleines Wrapper-Skript entwickelt, braucht vielleicht nicht alle Möglichkeiten der Bash, in anderen Fällen schmerzt aber der Verzicht auf moderne Features.

Bash-spezifische Syntaxelemente haben den Namen Bashisms (Bashismen) bekommen. Mit dem Hilfstool »checkbashisms« überprüft der Entwickler, ob er ohne Bash in ein Syntaxfettnäpfchen getreten ist. Eine gute Übersicht der verbotenen Syntaxkonstrukte und die Alternativen dazu vermittelt [4]. Manche Änderungen sind trivial, zum Beispiel wird das schlichte Bash-Konstrukt

function foo() {[...]
}

nur zu dem genauso simplen Dreizeiler:

foo() {[...]
}

Das Schlüsselwort »function« ist auch in der Bash ein überflüssiges Syntaxelement. Andere Beschränkungen schmerzen wirklich. Posix kennt zum Beispiel keine Arrays. Zwar erscheint die Bash im Vergleich mit Perl und Python arm an Datenstrukturen, mit dem Array aber verlieren Umsteiger auf die Dash die letzte Möglichkeit, Daten einfach zu strukturieren (Abbildung 1). Ein weiteres Beispiel: Für den Bash-Einzeiler »LIST=${PATH//:/ }« schlägt der Ubuntu-Wiki-Eintrag [3] den länglichen (allerdings nicht ganz äquivalenten) Dash-Code aus Listing 1 vor.

Abbildung 1: Die Dash kennt keine Arrays. Die Bash dagegen akzeptiert sie auch als »/bin/sh«.

Abbildung 1: Die Dash kennt keine Arrays. Die Bash dagegen akzeptiert sie auch als »/bin/sh«.

Listing 1

Aufspalten von $PATH in Dash

01 #!/bin/sh
02 LIST=$(awk '                             \
03   BEGIN {                                \
04     split( ENVIRON["PATH"], array, ":"); \
05     for (i=1;i<=length(array);i++) {     \
06          print array[i];                 \
07     }                                    \
08   }')
09 echo "$LIST"

Shells satt

Teil der Gemengelage um die Shellsyntax ist, dass das Unix-Universum viele Shells kennt. Wer Software von AIX-Systemen nach Linux portiert, stellt seine Skripte entweder von der Korn Shell auf die Bash um oder vertraut der installierten entsprechenden Linux-Variante. Bei vielen Distributionen gehört deshalb eine Korn Shell zum Standardumfang der Installation. Einen guten Überblick über die Unterschiede zwischen verschiedenen Shells samt einer kurzen Geschichte gibt [5].

Bei allen Unterschieden im Ressourcenverbrauch – für wirklich kleine Systeme kommen diese Shells alle nicht in Frage. Hier beherrscht Busybox [6] das Feld, die Software bündelt in einem einzigen Binary alle wesentlichen Unix-Tools inklusive der Shell Ash. Busybox ist Meister in der Bescheidenheit, denn Speicher ist auf Embedded-Systemen sehr knapp.

Fazit

Der Wegfall moderner Syntaxelemente in der Dash schmerzt. Zwar existieren Workarounds, aber der Skriptcode bläht sich auf, wird komplexer, schwerer zu warten und damit anfälliger für Fehler, Race Conditions und Sicherheitslücken. Es erscheint sehr fraglich, ob hier die Vor- die Nachteile aufwiegen.

Die Argumentation mit der Bootzeit jedenfalls greift zu kurz: Server werden sehr selten gebootet, da spielen ein paar Sekunden mehr in der gesamten Downtime keine Rolle. Auf dem Desktop wäre die Energie der Entwickler in ein stabiles Suspend und Resume wohl besser investiert, denn dies löst das Problem der Bootzeiten ohne funktionale Degradierung der Standardshell. (jk)

Infos

  1. Bernhard Bablok, Nils Magnus, “Neue Funktionen in der Bash-Version 4”: Linux-Magazin 06/09, S. 46, https://www.linux-magazin.de/Heft-Abo/Ausgaben/2009/06/Aufpoliert
  2. Dash: http://gondor.apana.org.au/~herbert/dash/
  3. Ubuntu-Wiki, “Dash as /bin/sh”: https://wiki.ubuntu.com/DashAsBinSh
  4. “How to make bash scripts work in dash”: http://mywiki.wooledge.org/Bashism
  5. “UNIX shell differences and how to change your shell”: http://www.faqs.org/faqs/unix-faq/shell/shell-differences/
  6. Multifunktions-Binary Busybox: http://www.busybox.net

Der Autor

Bernhard Bablok betreut bei der Allianz Managed&Operations Services SE ein großes Datawarehouse mit technischen Performancemessdaten von Mainframes bis zu Servern. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Linux und Objektorientierung.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 2 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
3 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Dargon
6 Jahre her

Suspend / Resume ist imho eine Krücke die eher abgeschafft gehört, und es ist die Schuld der Entwickler, wenn sie ihre Scripte mit “#!/bin/sh” auszeichnen, aber nicht POSIX-konformen Code verwenden. Der Artikel weißt die Schuld leider, wie sie oft, den völlig falschen zu. Wer auf Bash-Features nicht verzichten möchte, dem ist es unbenommen, diese auch weiterhin einzusetzen, nur muss er seine Scripte dann auch korrekt auszeichnen, indem er sie mit “#!/bin/bash” beginnt. Das funktioniert auch bei Boot-Scripten.

Oberlhrer
6 Jahre her
Reply to  Dargon

weißt -> weist

Gunther Klessinger
6 Jahre her

> Das Schlüsselwort »function« ist auch in der Bash ein überflüssiges Syntaxelement.

Einspruch, euer Ehren – es sollte, zumindest fuer libraries, die auch andere verwenden, IMMER function verwendet werden, und nicht der shorthand, da letzterer nicht “alias safe” ist.

Sollte z.b. alias bar="echo foo" gesetzt sein, wuerde das sourcen eines files mit syntax error crashen mit bar () { echo bar; } drin – waehrend function bar { echo bar; } keine Probleme macht.

Was besser ist sei dahingestellt – die Schreibweisen sind jedenfalls *nicht* identisch. Der Unterschied ist sicher einer der vielen nicht offensichtlichen gotchas beim shell scripten.

Last edited 6 Jahre her by Gunther Klessinger
Nach oben